创建没有nib文件的App

本文针对Cocoa Touch,Cocoa可能略有区别

首先说为什么不用Nib,其实也没什么太令人信服理由,大概就是以下几点吧

  • 代码重用比较方便
  • 用Interface Builder多多少少有丢三落四的毛病
  • 感觉自己写出来的代码更有存在感一些
  • 没事闲的

至于性能上是否有区别我还不太清楚,查了一会儿也没什么结果,如果各位对这方面有了解还请回复一下.

进入正题,以下几步搞定MainWindow

  • 删除Info.plist里面Main nib file base name这一条,或者把后面的MainWindow清空也行
  • main.m里有一句
    int retVal = UIApplicationMain(argc, argv, nil, nil);
    改成:
    int retVal = UIApplicationMain(argc, argv, nil, @"YOUR_APPDELEGATE_CLASS");
    如:
    int retVal = UIApplicationMain(argc, argv, nil, @"TestAppDelegate");
  • XXXAppDelegate.m里面,删除原来的一些内容,添加类似如下代码,这里就不细说了,相信都能明白
    – (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; //一定要initWithFrame,否则不响应触摸事件
        _mainTabBarController = [[MainTabBarController alloc] init];
        _window.rootViewController = _mainTabBarController;
        [_window makeKeyAndVisible];
        
        return YES;
    }
MainWindow搞定了,其他ViewController就比较简单了,可以重写init,也可以在viewDidLoad等方法里添加一些控件.创建ViewController就直接alloc init就行,不用管frame的问题.
 
没有了nib是不是感觉整个项目清爽了一些呢?不过要写的代码也变多了.
 
–以上–

解决因删除xib(nib)文件所导致的错误

用XCode编写程序,如果你由于各种原因需要删除一个xib文件,在删除文件后就可能遇到一些稀奇古怪的问题,首先说下解决方法

尝试做以下工作:

  • 粗暴地删除真机/模拟器上对应的App (这个一般就能解决问题了)(注意看后文,有更温和的解决方法)
  • 对整个工程Clean一下,至于Clean在哪,建议到Help下面搜索一下
  • XCode 3可能还有个Empty Caches… -> Empty

原因我想大家已经猜到了,编译一个新版本的应用只是把要修改的部分替换成新的,似乎并不会对删除多余的东西,比如你之前建立的xib(nib),这也就导致了虽然你的代码已经不在依靠那个nib运行了,但是原来的nib还是残留在那里,并且会在运行时加载,这就可能造成一些问题.

同样,如果你错误地删除了一个nib而没有"刷新"一下你的应用,这个错误就会被掩盖起来,等你的应用编译或安装到其他机器上时就可能产生各种错误.

了解了原理也就知道了替换第一步的放法:

  • 找到xxx.app,显示包内容,删除你不想要的nib文件

–以上–

[UIView beginAnimations:context:]与[UIView animateWithDuration:animations:]值得注意的一个区别

看过官方文档的都知道,官方推荐在iOS4以后使用[UIView animateWithDuration:animations:],而不是原来的[UIView beginAnimations:context:],来完成动画,虽然二者功能几乎完全相同,但使用前者在一些情况下会方便不少,这些内容可以参考官方文档View Programming Guide For iOS的Animation一节.

二者有一个值得新手注意的区别就是[UIView animateWithDuration:animations:]默认会禁止触摸,手势等的响应,这可以通过设置option选项来解决(直接引用StackOverFlow的一段了):

 

UIViewAnimationOptions options = UIViewAnimationCurveLinear | UIViewAnimationOptionAllowUserInteraction;

[UIView animateWithDuration:0.2 delay:0.0 options:options animations:^
 {
     highlightView.alpha = 1.0;

 } completion:nil];

 

就是这么一点事儿,害我走了不少弯路(我也是新手哈),在这里写一下,提示一下有可能遇到同样问题的人.

–以上–

在iPhone/iPad/iPod上查看iOS文档

7.26更新:iPad基本不用看了,拿模拟器试了下发现官方文档对iPad支持甚好,而离线版缺少对iPad的支持,无法通过Safari打开

苹果的官方文档甚是强大,对开发的各个方面都进行了及其详尽的介绍,作为开发者当然要时常翻看,本地的文档只能在电脑上看,要想用我们的iDevices查看文档只好连网看在线版的,但由于网速限制不够有效率,官方文档虽然支持转换成PDF,但是转换速度很慢,转换后一堆超链接也失效了.另外我找了好久也没有一个叫"Procket Documentation"或者类似的App,实在有些遗憾.

这里有一个不太完美的解决方案:官方文档虽然都是docset格式的,但其实右键显示包内容一看就是一堆html,css啥的,和在线版的文档一样完全可以用浏览器解决,所以只要把docset里面的Documents文件夹拖到你的iDevices里面就行了,如果你没有越狱就用GoodReader啥的看吧,不过我用iPhone 4测试效果不太理想,文档首页加载奇慢,而且显示不全,那个搜索不知道好不好使,太卡没法测试,iPad 2不知道效果能怎么样,有iPad 2的同学试验下最好在评论中说下效果,别的页面也可以直接浏览,不过效果没有桌面版的Safai那样好,排版似乎有些问题(不是大问题,试下就知道了),但是不影响看.

如果越狱了,可以在Cydia里搜索下lighttpd,安装,最好也安装一下Lighttpd SBSetting Toggle,安装后重启,然后就可以用Safari访问127.0.0.1了.(这里只介绍下安装那个Toogle后的使用方法,Lighttpd SBSetting Toggle的说明中提到webroot地址是/var/www,配置文件是/etc/lighttpd2.cong,用默认的配置文件访问127.0.0.1就可以直接看到/var/www下的目录.只要把之前提到的Document文件夹放这里就行了,接下来怎么做就不用说了吧.) 用自带Safari打开文档,文档首页依旧很卡,但是比GoodReader强多了,而且搜索也勉强可以用(iPhone 4测试,其他不知效果如何),其他页面和GoodReader浏览效果一样,都是排版有点问题,不影响效果.

目前为止也没做太多测试,现在正考虑用其他浏览器看看效果是否能好些(Operamini试了下根本不行),如果有发现就会更新这篇文章,没有就这样了.

其实这个方法也可以用来查看Android文档什么的.

–以上–

PS:搜狗输入法Mac版真TM2(Ver 1.1),中文标点那个选项没有记忆,切个输入法就又回到默认值了,写这篇文章净折腾全角半角的问题了.另外所谓的词库也就那回事,跟Win版没法比.

推荐:XCode 4视频教程

上一个项目做的差不多了,所以这几天尝试了下XCode 4,发现4与3的区别蛮大的,快捷键也改变了不少,一些功能也不知为何被精简了,比如User Script(应该是被Code Snippet代替了,不过感觉没有Script强大). 看文档实在太麻烦,上网找了找相关资料,发现之前的Become Productive in XCode系列又专门为XCode 4做了10个视频,而且这回是免费的,果断下载过来分享,毕竟看视频要比看文档直观多了。

这里还有个快捷键的PDF

XCode 4的这几个视频虽然感觉不如3的那么经典,但是作为XCode 4的快速入门还是相当有价值的。如果上面那些由于某些原因无法下载,我这里还提供一个115的下载地址,Have Fun。

PS:这篇介绍XCode 4之前的文章都是以XCode 3为基础的,要查看XCode 3的有关文章请通过标签或搜索查询.

–以上–

NSLog对程序性能的影响

NSLog,既可以像printf那样方便地格式化输出,同时还能输出时间以及进程ID等信息,可谓调试利器.但是其实NSLog对程序性能也有不小的影响,在执行次数比较少的情况下可能看不出来什么,当短时间大量执行的时候就会对程序执行效率产生可观的影响.

我遇到的一种情况就是我在一个UIScrollView子类的layoutSubviews方法中输出了很多次log,而这个layoutSubviews本身又有相对繁重的工作要做,由于每次拖动这个UIScrollView都要调用很多次layoutSubviews,因此程序实际运行起来拖动体验就非常差,卡顿现象严重,多次测试发现注释掉所有的NSLog后拖动就变得正常了.

综上,当你疑惑是什么导致了你的程序运行效率很差的时候不妨注释掉那些NSLog试试,你的问题也许就迎刃而解了.

–以上–

iCloud 部分功能开始生效

iCloud虽说要到秋季才发布,但其部分功能已经开始生效了.

如果你同意了iTunes的最新条款并通过电脑或者其他设备在iTunes Store下载了新的音乐,App或书籍你的iDevices就将受到如下推送通知:You downloaded an app from your computer. Turn on Automatic Downloads to receive apps on this device without having to sync.

在偏好设置>>Store,就可以看到新的选项,如图所示:

可以让你的iDevices不需要与电脑连接而自动同步(不如说是下载)你通过电脑或者另一台iDevice在iTunes Store购买的音乐,App和书籍.详情可以参考官方的说明

–以上–

关于initWithCoder导致的内存泄露

initWithCoder一般是通过[NSUnarchiver unarchiveObjectWithData:NSData]调用的, 虽然我们可能感觉代码写得没有什么问题, 但用Performance Tool检测还会有leak. 如果你确定自己的代码没有任何问题, 那么就可以无视Performance Tool的检测. 之所以会检测到内存泄露, 一个可能的原因就是unarchiveObjectWithData:返回的是一个autorelease对象, 而在Performance Tool检测时, autoreleasePool还没有释放, 因此我们可以尝试直接在unarchiveObjectWithData:这个方法附近范围新建一个autoreleasePool然后release它(参考main.m中autoreleasePool的创建和release), 如果Performance Tool不再提示内存泄露, 那么就证明的确没有内存泄露了.

可以参考这里的讨论

–以上–

Objective-C: delegate的那点事儿

Delegate算是Objective-C的一大特性, 关于Delegate的基础就不多介绍了, 有兴趣的请参看文档.

这里仅对Delegate使用中的一些问题做点讨论

我们用Delegate很多情况下是基于多线程的,比如我们有一个ViewController在这个Controller里面进行了一个下载图片的操作,下载成功后需要通过protocol来现实下载成功, 但是当ViewController已经被release,而下载工作才结束, 那么下载工作的[delegate didFinishDownload] (暂且就这么命名吧) 就会产生一个异常,因为你给一个deallocated的对象发送了一个消息.

那么,如何解决这个问题呢,首先我们可能想到用if (delegate == nil) 来判断delegate是否存在,但其实这是不行的,因为已经dealloc的对象并不是nil.要知道Objective-C中给nil发送消息是可以的,所以如果这种方法可行,其实我们就根本不需要if这句,[delegate didFinishDownload] 给nil发送了一个消息也不会出现异常,因此这种方法只是重复了上面的错误.

还有一个叫[delegate respondsToSelector:SEL]来判断delegate是否响应一个Selector, 根据上一段的描述,我们也可以判断出这个也是不行的.这里额外提一点关于respondsToSelector的东西,要使用这个方法,必须有@protocol MyProtocol <NSObject>,因为respondsToSelector是NSObject的一个protocol方法.

既然要防止delegate被release,那么retain这个delegate是否可行呢?这么做虽然避免错误的发生,但是也产生了另一个问题,这就关系到Objective-C内存管理中的Retain Circle, 即:有A,B两个Object, A中有一个B的实例变量,B中又有一个A的实例变量,要release A就必须releaseA中的B,而要release B有必须release B中的A,这样就产生了一个Retain Circle,A B都不能被dealloc.解决Retain Circle的方法就是使用弱引用(weak reference),弱引用没有被引用的那个Object的所有权,也就不需要release它,从而解决了Retain Circle问题.为了防止Retain Circle的发生, delegate通常都是弱引用的, 因此我们一般不应该retain一个delegate.但是似乎有一个例外:NSURLConnection, 网上对其的讨论结果是:NSURLConnection会retain它的delegate,详细可以参考StackOverflow上的这个问题

似乎没有简单可行的方法来解决这个问题(至少在本文发表时我还没有找到),那么我们只能在通过程序结构的设计来解决这一问题了,对应不同的程序自然也就有不同的解决方法,我想到的一种就是在这个ViewConrtoller被release的时候,把下载方法中的delegate设置成nil即可(目前测试可行, 如有错误还请指正).

更新几种解决方法:

–以上–

UITableViewCell的selecte与deselect

首先要从一个我遇到的问题谈起,一个基于NavigationBar的App,开始时我有一个UITableViewController,其中每个UITableViewCell点击后都会push另一个ViewController,每次点击Cell的时候,Cell都会被选中,当从push的ViewController返回的时候选中的Cell便会自动取消选中(有动画效果)。后来由于某些原因我把这个UITableViewController改成了UIViewController,之后就产生了一个问题:每次返回到TableView的时候,之前选中的Cell不能自动取消选中,经过查找得知:

UITableViewController有一个clearsSelectionOnViewWillAppear的property,

而当把UITableViewController修改成UIViewController后,这个属性自然就不存在了,因此我们必须手动添加取消选中的功能,方法很简单,在viewWillAppear方法中加入:

[self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];

即可,估计UITableViewController也是用类似的方法来实现取消选中的功能的。

–以上–