如何在Cocoa中设置文件的权限

这篇属于技巧性文章, 没什么营养.

如果你常写NSTask相关的东西, 可能需要设置一个脚本的权限为可以执行. 有各种Unix方法, 也有只用Cocoa就能实现的:

NSNumber *permissions = [NSNumber numberWithUnsignedLong: 493];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:permissions forKey:NSFilePosixPermissions];
[NSFileManager defaultManager] setAttributes:attributes ofItemAtPath:path error:&err]

简单解释一下那个"493", 这个就是十进制的"755", 也就是"rwx-xr-x", 所以如果你想要"777"的权限, 就用"511".

用NSFileManager创建文件的时候也可以传递上面的attributes, 创建可执行的文件.

–以上–

NSLog对程序性能的影响

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

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

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

–以上–

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即可(目前测试可行, 如有错误还请指正).

更新几种解决方法:

–以上–

Cocoa中的weak reference

以下所有内容翻译自官方文档,如有不当之处还请各位指正。

Weak Reference to Objects(弱引用)

————————————————————————————————————

retain 一个 object 创建一个 “Strong” reference,一个object在直到它所有的strong reference都release了之后才能dealloc,因此object的生命周期是受它的Strong reference的所有者控制的。某些情况下,这种行为不是我们所期待的,你也许想有一个不会阻止一个object dealloc的reference,这种情况下,你就拥有一个“Weak” reference,weak reference是通过存储一个不retain相应的object的指针所创建的。

 
weak reference在会有循环reference要创建时是必要的。比如,如果Object A和 B之间互相Communicate,它们各自需要一个对方的reference。如果它们各自retain了对方,那么直到它们之间的Connection切断之前它们之中任何一个都不会dealloc,但是它们之间的Connection要等待它们其中任意一个dealloc了之后才能切断,这就产生了一个矛盾。要解决这个问题,其中一个object作为一个次级(subordinate)角色并且拥有对方的一个weak reference。一个具体的例子,在一个view继承体系中,一个parent view拥有并因此retain他的childe views,但是一个child view不拥有它的parent;而又必须知道它的parent是谁,所以就有一个指向它的parent的weak reference。
 
Cocoa中另一些weak reference的例子包括但不局限于这些:table data sources,outline view items,notification observers,各种targets和delegates。(比如一个NSTableView不会retain它的data source,一个NSApplication不会retain它的delegate)
 
当你需要向一个你只拥有weak reference的object发送消息时,就要小心,那个object是不是已经dealloc了,如这这样会使你的程序Crash。
后面又是一些例子(delegate 和notification),不翻译了。