第6条: 理解"属性"这一概念
- "属性"是OC 的一项特性, 用来封装对象中的数据.
- OC 对象通常会把所需要的数据保存为各种实例变量.
属性特质
- 原子性
- 读/写权限
- readwrite
- readonly
- 内存管理语义
- assign: 针对"纯量类型", 例如 CGFloat 的简单赋值操作
- strong: 定义一种"拥有关系", 先保留新值, 并释放旧值, 然后再将新值设置上去
- weak: 定义"非拥有关系", 既不保留新值, 也不释放旧值. 跟 assign 类似, 在属性所指的对象遭到摧毁时, 属性值会被清空
- unsafe_unretained: 语义和 assign 相同, 但是它适用于"对象类型", 表达一种"非拥有关系", 当目标对象摧毁时, 属性值不会被清空
- copy: 表达的所属关系与 strong 类似. 并不保留新值, 而是将其拷贝.
- 方法名
- getter=<name> : 指定"获取方法"的方法名. 例如:@property (nonatomic, getter=isOn) BOOL on;
- setter=<name> : 指定"设置方法"的方法名. 用法不常见.
要点总结
- 可以用@propetry 语法来定义对象中所封装的数据
- 通过"特质"来指定存储数据所需要的正确语义
- 在设置属性所对应的实例变量时, 一定要遵从该属性所声明的语义\
- iOS 开发时应该使用 nonatomic 属性, 因为 atomic 属性会严重影响性能
第7条: 在对象内部尽量直接访问实例变量
要点
- 在对象内部读取数据时, 应该直接通过实例变量来读, 而写入数据时, 则应该通过属性来写
- 在初始化方法和 dealloc 方法中, 总是应该通过实例变量来读写数据
- 有时候会使用懒加载初始化技术配置某份数据, 这种情况下, 需要通过属性来读取数据.
第8条: 理解"对象等同性"这一概念
按照 == 操作符比较出来的结果未必是我们想要的, 以为该操作符比较的是两个指针本身, 而不是其所指向的对象. 应该使用NSObject 协议中声明的"isEqual"方法来判断两个对象的等同性.
一般来说, 两个类型不同的对象总是不相等的.
NSObject 协议中有两个用于判断等同性的关键方法:
- (BOOL)isEqual:(id)object;
- (NSUinteger)hash;
NSObject 类对这两个方法的默认实现是: 当且仅当"指针值"完全相等时, 这两个对象才相等. 如果"isEqual:"方法判断两个对象相等, 那么其 hash 方法也必须返回同一个值. 但是, 如果两个对象的 hash 方法返回同一个值, 那么"isEqual:"方法未必会认为两者相等.
特殊类所具有的等同性判定方法
在编写判定方法时, 也应一并复写"isEqual:"方法, 后者的常见实现方式为: 如果受测的参数与接收该消息的对象都属于同一个类, 那么就调用自己编写的判定方法, 否则就交由超类来判断.
等同性判定的执行深度
是否需要在等同性判定方法中检测全部字段取决于受测对象. 只有类的编写者才可以确定两个对象实例在何种情况下应判定为相等.
要点
- 若想监测对象的等同性, 请提供"isEqula:" 与 hash 方法
- 相同的对象必定具有相同的哈希码, 但两个哈希码相同的对象未必相同
- 不要盲目逐个监测每条属性, 而应该根据具体需求来制定检测方案
- 便携 hash 方法时, 应该使用计算速度快而且哈希码碰撞几率低的算法
第9条: 以"类族模式"隐藏实现细节
"类族"是一种很有用的模式, 可以隐藏"抽象基类"背后的实现细节.
要点:
- 类族模式可以把实现细节隐藏在一套简单的公开接口后面
- 系统框架中经常使用类族
- 从类族的公开抽象基类中继承子类时要当心, 若有开发文档, 则需要首先阅读
第10条: 在既有类中使用关联对象存放自定义数据
要点:
- 可以通过"关联对象"机制把两个对象联系起来
- 可以关联对象时可指定内存管理语义, 用以模仿定义属性时所采用的"拥有关系"和"非拥有关系"
- 只有在其他方法不可行的情况下才应选用关联对象, 因为这种做法通常会引入难以查找的 bug
第11条: 理解 objc_msgSend 的作用
要点:
- 消息油接收者, 选择器,及参数组成. 给某对象"发送消息"也就是相当于在该对象上"调用方法"
- 发给某对象的全部消息都由"动态消息派发系统"来处理, 该系统会查出对应的方法, 并执行其代码