读《Effective Objective-C 2.0》总结

熟悉 Objective-C
1. OC 的起源

OC 的方法(本质上讲是消息)在运行时决定。使用函数调用的语言,由编译器决定。如果涉及多态,则用到虚函数表。

2. 少在头文件中引用其他文件
  1. 两个头文件互相引用会导致编译错误;
  2. 引用协议,超类时,无法使用前向声明(@class),只能引用头文件;
  3. 协议一般放在单独的文件中,委托协议除外。
3. 多用字面量语法
  1. 向数组中插入 nil 时,如果使用字面量会直接报错,如果是使用初始化方法,则 nil 以后的对象被忽略;
  2. 字典也是同理。
4. 多用常量,少用 #define
  1. 不在头文件里面声明预处理命令,防止被别的文件引用,static const 也不能用;
  2. .m 文件里面声明的变量,需要加上 static,不然会产生外部符号,重复的外部符号会导致编译错误;
  3. static const 定义只在类内部可见的常量;
  4. extern 定义全局常量,并使用类名作为前缀;
  5. 头文件中使用 static const 会在每个编译单元中出现一个字符串对象,而用 extern 定义的对象放在全局符号表中。参考链接:static const Vs extern const;
  6. 使用 FOUNDATION_EXPORTextern 更好,参考链接:“FOUNDATION_EXPORT” vs “extern”。
5. 用枚举表示状态和选项
  1. 按位或操作来组合的枚举使用 NS_OPTIONS 宏定义,不需要相互组合的用 NS_ENUM 来定义;
  2. 尽可能指定枚举变量的类型。

对象、消息、运行期

6. 理解“属性”这一概念
  1. 如果直接使用实例变量,系统会根据偏移量来确定它们的位置。如果后来新增了实例变量,会导致重新编译;
  2. @property 的作用是合成存取方法;
  3. @synthesize 的作用是指定实例变量的名字,目前默认会执行 @synthesize xxx = _xxx;
  4. @dynamic 的作用是不要创建实例变量,也不要创建存取方法。
7. 在对象内部尽量直接访问实例变量
  1. 直接访问实例变量不会经过方法派发,不会触发内存管理语义,不会触发 KVO,无法调试(加断点);
  2. 可以考虑通过直接读取实例变量来加快读取速度;
  3. 初始化方法中不要调用 setter,这是因为子类有可能重写设置方法。参见 Demo 的 EOCSmithPerson.m 文件;
  4. 如果属性是惰性初始化的,必须通过 getter 来读取。
8. 理解对象等同性
  1. 应该保证相等的对象具有相等的 hash 值,但即使 hash 值不同,也不影响判断对象等同性。但是考虑到对象加入集合中的情况,我们总是应该为相同的对象提供相同的哈希值;
  2. 要么确保对象的哈希值不依赖内部可变的状态,要么确保依赖的状态不会改变。
9. 以“类族模式”隐藏实现细节
  1. 用类族模式可以将实现细节隐藏,返回基类的对象,并且通过多态完成任务。这其实是一种工厂模式;
  2. 注意此时调用 isMemberOf: superClass 的返回结果是 NO,因为对象实际上是子类的实例。调用 isKindOf: superClass 的返回结果则是 YES
  3. [arrayInstance class] = [NSArray class] 的返回结果总是 NO,因为左边一定是 NSArray 的子类。
10. 使用关联对象
  1. 使用静态全局变量作为键,设置好合适的内存管理语义。
11. objc_msgSend
  1. 大部分消息调用使用了 objc_msgSend 函数。对于某些边界情况,运行时环境可能使用一些其他的函数来处理:objc_msgSend_stret:如果返回的是结构体,调用这个函数。objc_msgSend_fpret:如果返回的是浮点数,调用这个函数。objc_msgSendSuper:如果给超类发消息,调用这个函数。
12. 消息转发
  1. 分为三个步骤,动态方法解析,快速转发和完整转发;
  2. 实现一个“字典”对象:TBD。
14. 理解“类对象”
  1. 运行期检视对象类型被称为“内省”;
  2. 因为类对象是单例,所以可以直接通过 == 判断类对象是否相等。但应该避免这么做,推荐使用类型信息查询方法(isMemberOfisKindOf)。TBD:举例说明

接口与 API 设计

15. 用前缀避免命名空间冲突
  1. 前缀至少是三个字母,两个字母的前缀由 Apple 保留;
  2. 全局函数和变量需要注意避免冲突;
  3. 在自己开发的库中,为用到的第三方库添加前缀。
16. 全能初始化方法
  1. 设置一个全能初始化方法,别的初始化方法都调用它。
17. 实现 description 方法
  1. 实现 description 方法,改变 NSLog 时的输出结果;
  2. 实现 debugDescription 方法,改变 po 时的输出结果。
18. 尽量使用不可变对象
  1. 不要把可变的 collection 作为属性公开,应该公开一个不可变的 collection 然后提供相关的修改方法。
19. 命名方式
  1. 如果返回值是新建的,首个词是返回值类型;
  2. 不要使用 str 这种简称,使用 string
  3. get 前缀仅在由“输出参数”保存返回值的方法中使用。
21. 错误类型
  1. 严重的错误可以直接抛出异常(比如禁止调用父类的方法,需要子类重写);
  2. 一般的错误使用 NSError,指定 domain(全局常量)、错误码(枚举类型)和用户细信息。
22. NSCopying 协议
  1. 实现 NSCopying 协议,重写 copyWithZone 方法;
  2. 容器的拷贝总是浅拷贝,copymutableCopy 的区别只是返回对象是否可变。

协议与分类

23. 使用委托
  1. 调用 delegate 中的方法时,总是应该传入发起委托的对象。这样代理对象可以判断不同的委托者。
24. 使用分类
  1. 可以把所有的私有方法都归入名叫 Private 的分类中。
25. 为第三方分类添加前缀
  1. 分类中定义的方法可以多次互相覆盖,以最后一个分类为准;
  2. 向第三方类中添加分类时,为分类名称和方法名称添加前缀。
26. 分类中不要声明属性
  1. 分类中无法创建实例变量;
  2. 可以定义存取方法,但是不要定义属性。

你可能感兴趣的:(读《Effective Objective-C 2.0》总结)