在iPhone项目里使用了几个UIImage的Category代码,为UIImage类直接增加了Alpha,Resize,RoundCorner的功能,升级了xcode 4.3正式版以后,再编译的时候这个代码会提示Category is implementing a method which will also be implemented by its primary class,网上查了一下,找到了一个不是很靠谱的答案,但是却可以解决问题。
在这几个类的.m文件里,顶上都有一个Interface UIImage(),里面定义了一个或几个私有方法的定义,然后在下面的Interface UIImage(Resize)里对这个私有方法做了实现,报警告的就是这个私有方法。
解决方案非常奇特,随便新建一个.h头文件,把Interface UIImage()和它里面的私有方法都移到这个头文件里就好了,把这几个类里面的相关的几个私有方法都合并进去,而且,你甚至不需要在其它文件里对这个头文件做引用,再编译的时候就不会报警告了,而且,原有的方法完全不受影响,在实现和使用这几个私有方法的地方也不会报找不到定义的错误。不知道xcode是如何使用这个没有被任何地方引用的.h文件的。
现在的互联网是开放的互联网,你要想融入这个开放的环境,当然自己也要开放一点。如果你希望别人能够很方便的使用你的网站上的用户及数据,当然要提供比较开放一点的接口。如果能够实现标准的OAuth认证接口,第三方用户在开发的时候就会容易很多,因为他们很可能在开发微博程序的时候已经做过同样的事写过很多这样的代码了。
不过C#实现OAuth认证还真是挺烦的,主要是,类库有好几个,但是,都找不到文档。适用于客户端实现的文档倒是有很多,适用于服务器端的真的没有。连类库的官方文档都找不到,只有源码里仅存的一份简易Test可以作为参考。费了九牛二虎之力终于算是实现了一个几乎一模一样的OAuth认证接口,记录一下。
经过搜索,比较,读资源,读源码,再比较,最后选择了oauth-dot-net这个类库。虽然它已经一年多没更新了,曾经承诺的2.0接口也没有动静,但是,它的Server端实现算是最容易理解和操作的了。
下载源码也好,Release版本也好,把一堆dll引入到自己的项目中。关于这个项目的类型,直接用VS新建网站即可,不需要web application,也不需要web service,也不需要WCF。但是呢,使用过程中你需要新建几个类,而且这几个类需要在web.config中指定,所以它需要完整的命名空间,简单起见,新建一个类库,把所有的类定义和通用的方法放到这里面,当然,因为我们的项目原本就有一个用来定义类变量和公共方法的dll,就直接放一起了。
然后你需要实现几个东西:ConsumerStore,CallbackStore,TokenGenerator,TokenStore,它们分别实现自己的名字前面加上I的那个接口。具体如何实现以及每个函数的作用,可以参考源码中提供的InMemoryXXX的实现方式。具体来讲,consumer是一个个应用,或者第三方的接入网站,每个应用有个唯一的appkey和appsecret,跟新浪的应用一样,你需要在这儿实现根据appkey查找该consumer的功能,每次请求发起,都需要调用。因为创建应用一般来说有自己的流程,所以在这个store可以不用管它。callback是调用者的返回URL,在申请request_token的时候会传递过来,需要保存下来到用户亲自授权完成以后再读出来进行跳转。TokenGenerator只有两个函数,用来生成新的request_token和access_token,如何生成它里面的key和secret,随便你自己,只要永远不重复就可以了,嫌麻烦就直接用GUID。TokenStore是用来查询token的,比如根据tokenkey查找request_token或者access_token,你可以用自己的数据库来实现,字段也就那几个,也是以后每个请求都要读取的。但是它自己的实现接口里面忽略了很重要的一点,就是没有把access_token跟用户Id绑定起来,这个一定要自己实现的,不然程序传一个access_token过来,你找不到是哪个合法用户。
实现了这几个类以后,在源码里面找到OAuth.Net.Examples.EchoServiceProvider这个示例项目,把它的web.config里面的oauth.net.serviceprovider和oauth.net.components两段配置信息拷到自己的web.config里面来,然后修改oauth.net.components这一段里面对应的四个自己已经实现的类,指向自己的类库的位置。
然后添加一个global.asax,对于网站项目,这个文件是没有codefile文件的,可以直接在asax下面写代码,把示例项目中的那个Init方法复制过来。这时候,基础工作就完成了。
然后需要实现三个过程,申请request_token(这一步没有界面,对方直接用代码读你的URL获取这个信息),用户跳到authorize的页面,输入用户名和密码,验证并授权给该客户端,验证完成后返回用户指定的callbackurl,用户再拿该返回链接中的参数申请access_token(这一步也没有界面,通过程序间完成),保存下你返回的token和secret就可以用在其它所有的读取数据的请求上了。
示例中已经给你准备好了一个RequestTokenHandler.cs和AccessTokenHandler.cs,不过这两个示例除了把你搞哭没啥大作用。
新建一个request_token.ashx文件,像RequestTokenHandler.cs一样继承OAuth.Net.ServiceProvider.RequestTokenHandler这个类,实现里面的IssueRequestToken方法,这个时候,建议你就不要再参考示例项目里面的这个代码了,而去OAuth.Net.ServiceProvider.RequestTokenHandler这个类里面,参考它的IssueRequestToken方法。因为示例项目里面的token和secret都是写死的(包括consumer也是写死的),因此它没有对生成的token做任何保存的动作。实际上,你需要将这个token保存到数据库里,同时保存callbackurl,至于要调用CallbackStore来保存还是你直接操作自己的数据库字段,无所谓。这个文件最后是把生成的token和secret以纯文本的形式返回给客户端。
然后客户端把得到的token字符串作为参数,跳到你的authorize.aspx页面,这里你需要提供用户的登录框,和一个授权按钮。当用户点下授权按钮的时候,检查用户名密码是否正确,然后根据用户跳转过来的时候传过来的那个oauth_token参数,找到你前面生成的request_token对应的其它字段(如果你要对request_token设置有效时间,这里需要判断该时间),读出其中的callbackurl字段,然后,调用ServiceProviderContext.VerificationProvider.Generate()方法根据该request_token生成一个oauth_verifier字符串,将该值附加到callbackurl的后面,跳转过去。但是在跳转过去之前,你必须做一件事。
用户的callbackurl页面收到你的oauth_verifier以后,再连同前面的request_token一起来访问你的access_token.ashx,申请access_t0ken。所以用户端也必须保存你前面生成的request_token才可以(临时性的,每次授权前读取,申请到access_token就作废,所以一般用session来保存就行了)。对于access_token.ashx的内容,源码同样提供了一个例子,不过同样是个一点用都没有的例子,因为它完全没有涉及到授权和生成全局唯一access_token的过程。你可以让你的access_token的handler继承自OAuth.Net.ServiceProvider.AccessTokenHandler,然后重写IssueAccessToken方法。这个方法其实很简单,因为父类已经帮你检测了传过来的所有的参数以及签名的正确性,甚至连access_token和verifier是否匹配都验证过了,所以你要做的就是把对应的access_token返回给用户。你可以生成一个新的access_token返回给用户,因为它里面包含的token和secret其实就是两个普通的字符串,跟前面用到的所有的变量都没有直接关系,你只要保存token是唯一的就可以了,因为以后用户做所有的请求,实际都只使用这个token字符串作为自己的唯一身份标志。所以,理所当然,你要把生成的这个access_token永久性的保存到自己的数据库里,还要把这个token跟进行授权的用户的id关联起来,以便以后每次可以查询到这个用户的身份,才能传递他的数据回去。
等等,客户端在申请access_token的时候,只传递了request_token和一个verifier的字符串过来,授权的用户的信息并没有再传回来,你如何知道把哪一个用户关联到这个access_token上?
所以,你需要把创建新access_token的过程提前,放到authorize的时候,在跳转回callbackurl之前,直接在这里创建一个新的access_token,将它和你的request_token还有你的用户的信息关联起来,保存到数据库里。然后在用户申请access_token的时候,你只要根据传递过来的request_token去数据库里找到前面生成的access_token,把两个值返回给客户端就OK了。
现在,认证过程已经完成了,用户可以使用你的数据接口了。
示例中也为你准备了一个echoHandler的测试代码,但是,悲剧的是,这个代码并不包含任何身份验证的过程,是个完全干净的独立的handler,不知道开发团队提供这样一个示例代码的意义何在?
你可以自己新建一个echoHandler.ashx(或者随便什么名字),你需要以前面的OAuth.Net.ServiceProvider.AccessTokenHandler作为模板开始你的工作。将它的代码完整的复制到你的handler文件里面来,慢慢修改。首先,ParseParamters里,检查参数完整性,把VerifierParameter删掉,同样的把this.CheckVerifier(context, requestContext);也删掉,verifier参数只在申请access_token的时候才会出现。然后把parameters.AllowOnly检查过程删掉,原因很简单。然后修改SetRequestToken函数,它的代码是通过token参数找到request_token对象,把它改成通过token参数读取你的数据库里的access_token对象(当然,同时把它关联的用户的信息也读出来),然后,把IssueAccessToken函数的过程改成你的业务处理过程,比如输出个用户详细信息啥的。至于输出成json还是xml,就看你的个人喜好和用户的要求了。每个业务处理的handler的返回格式实际是无固定格式的,因为这毕竟是跟你自己的网站业务紧密相关的。
这个时候,你的网站的标准的RESTful格式的api接口就准备好了,如果用户有开发新浪微博客户端的经验,同样的代码拿过来稍微改改就能完成你的网站的OAuth认证,然后就可以方便的调用你的其它的数据接口了。(不过新浪现在已经开始推广OAuth2.0接口,而oauth-dot-net类库目前还没有提供2.0相关的验证方法。如果你有兴趣,可以自己实现一下2.0的交换参数的过程,然后,告诉我一下。
写这样的文章容易露怯,因为我不知道什么是业界真正规范的做法,这里写的只是自己认为比较简单易用的最佳实践,写出来恐怕会被大牛笑话,也影响自己的多年码农的形象。不过方法越多,新人越难掌握,所以提示一下也是好的,欢迎探讨。
其实这个题目里包含了两个东西,传递变量和调用方法,这两个的处理是不一样的,因为之前看了CocoaChina上的那篇文章是混在一起写的,所以这里也放在一篇文章里写。
变量传递
通常就是指要在一个ViewController里面读取到另外一个ViewController中的变量,比如弹出一个新界面的时候,需要知道原来的界面当前的SegmentControl现在选中的是哪个状态之类的,当然也有更复杂的,全局通用变量的处理也在这个范畴内,很多View都要用到同一个变量,比如当前登录用户的身份。
这里最简单的方式有两个,一是通过文件方式传递,当然,你不用自己实现读写文件,只要使用NSUserDefaults就可以了,它里面可以放进任何能够被序列化的简单变量,全局都可以读取,因为这个类本身是提供单例模式访问的,只要在任何地方修改了某个key对应的value,调用一下同步函数,这个值就被写入磁盘了,在任何其它的地方重新读取这个key,就能取到最新的value。而且这个值是永久保存的,程序重启也没关系。
第二个方法是通过appDelegate,它也是全局的,本身提供单例模式访问,所以不必担心访问到同一个类不同对象的不同变量值。只要你在AppDelegate.h里面定义的变量,在任何ViewController里面,获取[[UIAppcation sharedApplication] delegate],再调用它的该变量的名字就可以了,取值或者赋值都无所谓,该变量对整个应用程序都是可以访问的。系统提供的CoreData模板中Context的访问方式就是这样实现的。
还有第三个方式,跟下面的方法调用一起说。
方法调用
方法调用就是指在一个ViewController里面需要调用另外一个ViewController里面的某个方法,事件也可以归为这一类,比如当前端的这个ViewController完成了处理任务需要关闭的时候,要求它后面即将需要显示出来的那个ViewController刷新自己的页面,以便把最新的数据显示出来。
实现事件调用的办法最标准的是delegate。定义一个新的delegate接口,里面是某个方法或者事件的定义,不包含具体实现。被调用一方的ViewController实现该接口,并实现该方法的具体内容。调用一方需要声明该delegate类型的一个变量,在需要调用的时候,直接把这个变量当成被调用的那个Controller类来用,比如myDelegate.DoXXX(); 这样做了之后,在调用方的ViewController实例化的时候,需要将被调用方的实例赋值给它的myDelegate变量才行,要不然它就是个空指针了。这种实现方式之所以标准,是因为对调用方没有约束,这个类可以方便的重用,也就是松耦合设计,以后其它的项目可以把这个类复制过去,一行不用改,只要保证有别的类实现了这个delegate并被传递进来就可以了。
另外一种简化的方法是,直接将被调用方的类定义成调用方的一个变量。比如在ChildController里面定义一个变量RootViewController myRoot;然后实例化该类的时候直接child.myRoot=self;这时候在child内部,对myRoot这个变量是可以为所欲为的,它里面的方法你可以随便调用,包括刷新它自己的界面,或者改变某个控件的状态和值,或者改变它里面的某个变量的值(这就是上面说的传递变量的第三种方法,仅限于两个Controller之间),都是可以的。不过这时候就是紧耦合了,这个ChildController不方便拿到别的项目里去用,因为它里面把RootViewController的方法给写死了。不过如果你也没打算以后在别的项目里重用它,这样做可以省掉不少代码。不过同时你还得小心被你调用的那个rootController,此时有没有被系统给释放掉。如果在child里面定义变量或者传递变量过来的时候把它retain了,一般不会出现这种问题。
OK,就说这么多。
WikiOffline 是一款运行在iOS上的可以让你查看完整离线维基百科文件的App,不过使用内容之前需要将下载的维基离线dump文件转换一下格式,而且它的转换程序是windows的,写个简要说明在这儿。
1. Use iTunes File Share to get the install files. Copy these two files to your windows.
2. Run setup.exe to Install it.
3. Run the WikiOfflineConverter tool.
4. Click the link, Select the wiki language you want, and download the pages-articles.xml.bz2 file.
5. When download finished, use convert tool browse and select it. Then click Convert button.
6. Go to the install folder find the two files, upload them to app’s document folder use itunes’s file share.
7. Now, you can open the app in your iphone or ipad and search and read it.
最后附上我已经转换完成的最新的6月28日版中文离线dump文件:http://u.115.com/file/e6vjyqox 大家直接下载,可以省下转换的时间了,因为转换确实很慢。
安装了一下WP7的开发环境,问题多多。
先安装了VS2010旗舰版,再安装开发工具,提示不支持win2008,修改了安装文件跳过去,开始自动下载了359M的安装文件,结果安装到第9/10步的时候卡死,到进程里kill掉一个补丁的进程,安装程序继续,但是最后一步仍然卡住,不停的跳出存在另一个安装进程的对话框。最后kill掉整个安装进程,重新开始安装,有三个选项,使用第一个选项“安装遗漏的组件”,自动开始安装前面被跳过的最后一个组件,最后终于安装完成了。
然后安装Update1补丁,进程里总是有个xde.exe占满CPU,kill掉了几次都跳不过去,索性不管它了,几分钟后安装成功。
然后再安装VS2010的一个补丁,跟上一个问题一样。
全部安装成功以后,发现开始菜单里除了原来的VS2010的旗舰版之外,又多了个VS2010 Express for Windows Phone,这破组件不会自动识别版本高低的啊?
唯一舍得安慰的是,创建了一个Hello world项目以后,点击运行,顺利的启动了模拟器,把程序跑起来了。不过,这模拟器咋这么慢呢?
换了新的Macbook Pro以后,犹豫两三还是决定不装双系统了,日常的windows的工作就用虚拟机来搞定吧,比较安全,而且切换起来比较快,可以结合两个操作系统的软件优势,协作互补。鉴于以前在windows上一直使用virtual box,于是在Mac也延续使用下去,把原来windows上的虚拟机文件拷过来,打不开,新建虚拟机,使用原来的vdi虚拟硬盘,没有问题,一切正常。但是用了没几天,性能问题实在是有点受不了。整个系统就是反应慢一拍,浪费了这i7的CPU了,而且在virtual box的设置里面设了两个CPU,结果进系统看还是只能看到一个CPU。(virtual box 3.2.8,Guest系统是windows 2003 server)。另外接USB外接硬盘问题比较严重,必须在关机状态下添加到虚拟机的USB设备里,启动后插入才能自动连接到虚拟机里面,新拿过来的硬盘就没有连接到虚拟系统里去。而Mac系统又不能读写NTFS的外接硬盘。(还没装ntfs-3g)。另外切换全屏的过程中还崩溃了几次。
以前也在Mac下用了一阵子vmware fusion 3,相对来说要方便一些,每次连接USB设备都会自动询问是连接到主机还是虚拟机,整体的性能也算不错,但是有一套经常使用的virtual box的虚拟机,里面安装的环境比较复杂,涉及到另一个公司的内部VPN,不太好迁移,而vbox和vmware又不能同时启动,会导致其中一个直接挂掉,所以这次也没有办法把完整的环境切换到vmware上面来。
现在实在是受不了vbox的性能了,于是搜索了一下google,发现parallels desktop for mac 5的口碑不错,论坛上普遍评论其性能要高过vmware,甚至可以玩3d游戏,虽然最多只有20帧。不过这个软件似乎破解起来不太容易。官网直接下载,可以申请10天的试用序列号。
parallels的第一大优势,是导入其它虚拟机系统。virtual box和vmware都支持。选择了vbox的虚拟机的xml文件以后,提示文件格式不正确,可能还不能兼容这么高版本的,于是直接选择它的vdi虚拟硬盘文件,分析后正确的得出其中安装的操作系统版本,开始转换,完成后自动创建了对应的虚拟机系统,直接启动就可以了。第一次启动会花点时间进行配置,配置过程完全透明,在这个过程中,后台悄悄完成了虚拟系统中的Tools驱动的安装。启动后还自动把开始菜单里面可用的程序添加到了Dock上面一个文件夹,随时可以在Mac里打开windows里面的程序,再配置它默认的透明模式,windows程序直接将窗口完整的显示在Mac界面下,与Mac自己的程序毫无二致。不过我还是比较习惯虚拟机用窗口模式。如果要转换的虚拟机有多个硬盘,需要分开转换,转换完系统盘以后,再转换第二硬盘文件,分析后会提示该文件里面没有安装操作系统,是否转换成自己格式的硬盘文件,选是,转换完成后添加到前面的虚拟机配置里面就可以了。不过原来vbox的固定分配大小的虚拟机文件转换后成了动态大小。
parallels的第二大优势,就是运行速度。它终于可以正常的使用多CPU了,分配了三个CPU给虚拟系统,可以正常识别及使用,整体的运行效率高了一大截,磁盘效率也没有觉得比原来低,甚至比vbox的固定分配大小的硬盘还要快(打开我的电脑的直观感受)。根据网友测试的数据,其虚拟系统的各项得分并不比装双系统差。而且号称完美支持win7的aero特效,不管是窗口模式还是透明模式。在设置项中有多个选项用来提升虚拟机的性能,或者平衡与主系统的性能。
有这两条,已经足以让我痛快的卸载掉vmware和virtual box了。不过貌似有个缺点,虚拟系统里面的CPU占用率明明很低,但是在Mac系统下看虚拟机的CPU占用却很高,导致CPU温度很高,试一下改成只分配两个CPU会不会好一些。













近期评论