首页 > 代码库 > Effective Objective-C 读书笔记
Effective Objective-C 读书笔记
一本不错的书,给出了52条建议来优化程序的性能,对初学者有不错的指导作用,但是对高级阶段的程序员可能帮助不是很大。这里贴出部分笔记:
第2条:
使用#improt导入头文件会把头文件的内容全部暴露到目标文件中,而且如果两个类之间存在循环引用则会出现编译错误,所以要尽量使用@class进行类声明。
如果需要实现一个协议,则必须#improt这个协议的头文件,所以可以将协议单独定义在一个.h文件当中。如果这个协议是代理模式协议的一部分,即需要与类捆绑使用才有实际意义,则建议定义在类当中,并以类名为前缀。
第4条:
尽量不要在头文件当中使用#define定义常量,因为这样一来,引用这个头文件的所有代码都会进行宏替换,#define的作用域就被放大到了全局,即不安全,也失去了其在特定文件当中的特定含义。
一种取而代之的做法是,不要将这类私有的常量定义在.h文件中,而是在.m当中通过static const修饰该常量。如此一来,该常量就仅仅会出现在其所在源代码对应的目标文件当中了。
在这种定义当中,const表示常量,不能被修改,static则是为了限定范围,如果不加static,则默认是extern类型,那么就会和外部的同名变量发生冲突。实际上对于编译器来说,static const的行为和宏定义的行为方式完全一致,都是字符串替换。
如果一个类希望对外(如通知中心)暴露一个常量来使用,则需要在其.h文件当中使用extern NSString *const myString;这样的修饰符来声明,并在.m文件当中使用NSString *const myString = @”xxx”;这样的语法来定义它。这表示指针指向的内存地址不能被改变。对于这类型的常量,由于它要放在全局符号表当中,所以要以类名开头,以避免冲突。
第6条:
尽量不要使用_开头的字段,而是使用属性。因为使用字段相当于对变量地址的硬编码,一旦增加字段就需要重新编译,令类的扩展性下降,也会带来版本问题。而使用属性则会将这些偏移量交给类来管理。
第7条:
在跨对象访问成员时,最好访问属性而不是字段,这样不会绕过对象的内存管理和KVO。但在对象内部访问成员时,则推荐使用字段访问,能够绕开方法派发来提升访问性能。但要注意该copy的地方不要直接等号赋值。另外,在init和dealloc方法当中不建议使用属性访问字段。
第8条:
==符号只会按照内存地址来判等,而不会管是否是同一类型(父、子类),所以要复写ieEqual方法,不但要判断地址,还要判断类型:
if ([self class] != [object class]) return NO;
对于字符串,建议使用isEqualToString这样的判断方法,它绕过了类型检查,性能更高。
第9条:
OC的类族是一种设计模式,实质是在抽象父类中定义一个工厂方法,它根据枚举来返回具体的子类,并且把子类要实现的方法全部定义在父类当中来提供统一实现接口,即“简单工厂+适配器”。
第10条:
给类扩展方法可以用Catalog机制,给对象扩展属性则可以使用Associated机制,它通过一个static的key给对象添加一个关联属性,比如可以将一个block关联到UIAlertView,可以使代码显得更加紧凑。
第11条:
OC是一门纯动态语言,它通过消息机制调用方法,这种方式调用的目标方法都是在运行时动态绑定的,而不是编译期对函数地址进行硬编码的。
当给一个对象发送消息时,实际是调用了一个objc_msgSend(接收对象,选择器,参数表…)函数,也就是消息派发机制。它会去对象的方发表里找到这个方法并调用,如果没有找到则沿着继承结构向上找。找到方法后,该方法会将匹配的结果放在一张快速映射表当中以提升下次执行的检索效率。如果最终都没有找到对应方法,则会进行消息转发。
第13条:
OC中每个类都有一个方法表,可以理解为key是方法名,value是具体函数对应的指针,叫做IMP,OC可以在运行时使用method_exchangeImplementations方法改变一个selector的IMP来动态替换方法的实现。
第15条:
苹果公司保留任何两个字母的前缀的使用权利,所以要尽量使用三个字母的前缀。
如果在你的前缀为XYZ的库里使用了前缀为ABC的第三方库,为了避免版本冲突等问题,请把ABC逐个改为XYZ。
第16条:
如果一个类有多个初始化方法,一定要提供一个最全面的初始化方法,并将所有与底层信息相关的初始化过程放在这个最全的方法里,这是为了屏蔽将来底层机制改变带来的影响。同样,子类如果需要初始化父类的内容,也应该调用父类的最全面的初始化方法。NSCopying协议同理。
第18条:
从网络读取来的属性尽量都标记为readonly,因为一旦set它的值可能会导致一些问题(比如NSSet内部重复),如果将来需要可写则修改这个特性即可。但是这也无法避免用户通过KVC来进行修改,所以尽量不建议使用这种方式修改属性值。同理出于安全考虑,尽量不要将可变的集合作为属性对外公开。
第20条:
不要给私有方法添加_前缀,这是留给苹果公司用的。
第23条:
委托模式:A委托B做一件事的一部分(比如远程异步读数据),在A类的.h文件当中就需要定义一个Delegate协议,定义需要做些什么,并且让A类持有一个weak的代理对象delegate,之所以是weak是为了防止循环引用问题。
这个协议通常只有在B类内部使用,所以建议在B类的.m文件当中定义一个扩展来继承这个协议,当然,实现要放在implementation当中。
为了方便回调,协议的第一个参数建议是委托发起者A的指针。
最后只要将调用者A的代理对象delegate设置为某个B类的实例即可。
第26条:
尽量不要在Catalog里定义属性,这样的话无法生成get/set方法,需要增加@dynamic关键字防止报错。
第27条:
类扩展可以承载私有属性,也可以把.h当中声明的readonly属性改写为readwrite,这样在类内部使用get/set方法时可以触发KVO。另外,类扩展可以实现协议,则不会将类对实现的协议暴露给.h文件。
第29条:
release一个对象后,只是把他放回可用内存池,并没有立刻释放掉它,所以这个对象有可能还是碰巧可用的。为了防止悬挂指针,在手动release一个对象之后,最好将指针值设置为nil。
第30条:
ARC调用的内存管理方法实际上并没有走OC的消息派发机制,这样会消耗太多的CPU时钟周期,而是直接调用的底层C版本,这样性能更高,正因为如此,程序员不能直接调用引用计数管理相关的方法。
使用ARC时,无需书写dealloc方法,这是由ARC通过某些Objective-C++方式实现的。
第31条:
在dealloc方法当中,应该释放一切通知机制相关的内容!同样不要调用属性的getter、setter方法,因为这会触发KVO。
第33条:
在MRC下,对于int等类型,使用assign,对于对象,使用unsafe_unretained,虽然二者都不会增加引用计数,但是后者的意义更加清晰。
在ARC下,使用weak,只要对象被回收,对应的weak属性就会被设置为nil(而unsafe_unretained则不会,这是两者的不同)。
第42条:
尽量不要使用performSelector调用方法,因为这种方式是在运行期确定要调用的方法,所以ARC知道如果有返回值,这个返回值的内存管理方式是什么,所以ARC不会给返回值添加自动释放。另外,这个这个系列方法的参数类型和个数局限性很大。如果需要延迟方法执行,或者在主线程上执行,尽量使用dispatch_after和dispatch_async(dispatch_get_main_queue(), block),而不要用performSelector系列方法。
第43条:
NSOperationQueue的底层是GCD实现的。但是NSOperationQueue有其独到的好处:
- GCD无法取消一个任务,操作队列可以。
- 可以方便指定串行操作顺序。
- 可以用KVO监控isFinished等属性。
- 可以指定优先级。
- 可以自己创建NSOperation子类来承载数据。
通知中心的底层就是用NSOperationQueue来实现的,而不是用GCD。
第50条:
做缓存时优先使用NSCache而不是手写NSDictionary,它可以在内存低的时候自动清理长期不用的缓存,而且它是线程安全的,并且,无需我们手写缓存时考虑缓存的内容是copy还是retain,它是retain的。
第51条:
OC拥有类似java的静态初始化方法,一个是+(void)load,会在程序开始时执行一次,一个是+(void)initialize,会在第一次使用一个类时执行一次。