第47条 熟悉系统框架
- 第三方的framework并不是严格意义上的动态库,但系统的framework都是动态库
- Foundation 对应CoreFoundation。两者可以通过无缝桥接(toll-free bridging)相互转换,如NSString可以转为CFString。
CoreFoundation 如 通讯录里会用到。
第48条 多用枚举块,少用for循环
反向遍历:
NSArray *array = @[@1, @2];
for (NSNumber *obj in [array reverseObjectEnumerator]) {
NSLog(@"obj == %@", obj);
}
打印结果:2 1
第 49条 对自定义内存管理的对象使用无缝桥接
后面介绍了如何创建CoreFoundation中的字典,并转为Foundation中的字典。(此处省略)
第50条 创建缓存时用NSCache而非NSDictionary
当请求网络图片时,可以将图片缓存起来,可以放到NSCache里。
- NSCache 比 NSDictionary的好处是,当系统内存资源不够时,NSCache可以自动被释放,而且NSCache还可以优先释放不经常使用的缓存。
- NSCache 是线程安全的,开发者在不加锁时,多个线程可以同时访问 NSCache。
还有一些内容比较抽象,此处省略(后面有时间补上)。
第51条 精简 initialize 和 load 的实现代码
+ (void)load;
- 加入运行期系统的每个类和Category都会调用这个方法,且只调用一次。
- 若 Category 和所属的类都实现了该方法,都会执行(若普通方法就只执行 category 里的了,会把类里同名方法覆盖),先执行类里的,再执行 Category 里的。
- 在执行子类的load方法前,必定会先执行所有超类的load方法,而若代码还依赖了其他程序库,则程序库里相关类的load方法也会先执行。因此,在load方法里使用其他类是不安全的。如在A类的load方法里初始化一个B类的变量,无法确定B的load方法是否已经执行,若B未执行完load方法,B的变量可能无法正常使用。
- load 方法里的代码要精简,因为整个程序会在执行load时阻塞。事实上,load方法仅用于调试,如判断category是否已经正确载入。
+ (void)initialize;
- 该方法是惰性加载的,只有用到了该类时才会调用。这点跟load不同,load是必须等所有的类的load都执行完才能继续。
- 与load相比,initialize方法里可以安全的调用其他类。而且当前线程正在调用initialize方法,系统会先阻塞其他线程,等initialize执行完了再执行别的(个人理解,有点像automic)。
- 若Student类继承于Person类,若Student里未实现initialize方法,那么Student在初始化时也会走一遍父类里的initialize方法。如下代码,会打印两次:
Person里:
+ (void)initialize
{
NSLog(@"%@ -- initialize", self);
}
打印结果:
Person -- initialize
Student -- initialize
所以一般该方法都会加一个判断,这样若子类未实现的话就不会打印两次了。
+ (void)initialize {
if (self == [Person self]) {
// ... do the initialization ...
}
}
-
initialize也要尽量精简,只做内部配置,不要调用其他类的方法,甚至本类的方法最好也别调用,防止耦合,如下情况就可能会造成错误:
A类里面initialize里用到B类,所以会先去加载B的initialize方法,而B里也用到了A,此时A并未初始化结束,所以其调用的方法可能会有错误。
内部配置:如某些oc的对象(如NSMutableArray)无法在编译器初始化(如下图会报错),就可以放到initialize里。但是NSString可以在编译器初始化。
第52条 NSTimer 会保留其目标对象
若在dealloc里取消定时器是不正确的,因为定时器强引用了当前类,导致dealloc根本没被调用。