前篇回顾:
Effective Objective-C 2.0 再读笔记(一)
Effective Objective-C 2.0 再读笔记(二)
第12条: 理解消息转发机制
消息转发机制分为两大阶段. 第一阶段先征询接收者, 所属的类, 看其能否动态添加方法, 已处理当前这个"未知的选择子"(unknown selector), 这叫做"动态方法解析"(dynamic method resolution). 第二阶段涉及"完整的消息转发机制"(full forwarding mechanism). 若果运行时系统已经把第一阶段执行完了, 那么接收者自己就无法再以动态新增方法的手段来响应包含该选择子的消息了. 此时,运行时系统会请求接收者用其他手段来处理与消息相关的方法调用. 这又细分为两小步. 首先, 请接收者看看有没有其他对象能处理这条消息. 若有, 运行时系统就会把消息传达给那个对象, 于是消息转发过程结束, 一切正常. 如果没有"备援的接收者"(replacement receiver), 则启动完整的消息转发机制, 运行时系统会把与消息有关的全部细节都封装到 NSInvocation 对象中, 再给接收者最后一次机会, 令其设法解决当前还未处理的这条消息.
动态方法解析
对象在收到无法解析的消息后, 首先将调用所属类的下列类方法:
+ (BOOL)resolveInstanceMethod:(SEL)selector
该方法的参数就是那个为止选择子, 期返回值是 Boolean 类型, 表示这个类是否能新增一个实例方法用以处理此选择子. 在继续往下执行转发机制之前, 本类有机会新增一个处理次选择子的方法. 假如尚未实现的方法不是实例方法而是类方法, 那么运行时系统就会调用另外一个方法, 该方法与"resolveInstanceMethod:"类似, 叫做"resolveClassMethod:".
备援接收者
当前接收者还有第二次机会来处理未知的选择子, 在这一步中, 运行时系统会问它能不能将当前消息传递给其他接收者来处理. 与该步骤对应的处理方法是
(id)forwardingTargetForSelector:(SEL)selector
方法参数带代表未知的选择子, 若当前接收者能找到备援对象, 则将其返回, 若找不到, 就返回 nil.
完整的消息转发
首先创建 NSInvocation 对象, 把与尚未处理的哪条消息有关的全部细节都封于其中. 此对象包含选择子, 目标, 及参数. 在触发 NSInvocation 对象时, "消息转发系统"将亲自出马, 把消息指派给目标对象. 次步骤会调用这个方法
- (void)forwardInvocation:(NSInvocation*)invocation
这个方法实现的很简单: 只需改变调用目标, 使消息在新目标上得以调用即可.
要点:
- 若对象无法响应某个选择子, 则进入消息转发流程
- 通过运行时的动态方法解析功能, 我们可以在需要用到某个方法时, 再将其加入到类中.
- 对象可以把无法处理的某些选择子交给其他对象来处理
- 经过上述的两个步骤之后, 如果还是无法处理选择子, 那就启动完整的消息转发机制
第13条: 用"方法调配技术" 调试"黑盒方法"
此方法是利用运行时系统来交换两个选择子的具体实现方法, 主要有两个步骤:
- 根据给定的选择从类中取出与之对应的相关方法:
Method class_getInstanceMethod(Class aClass, SEL aSelector)
- 交换两个方法实现:
void method_exchangeImplementatios(Method m1, Method m2)
要点:
- 在运行时, 可以向类中新增或者替换选择子所对应的方法实现
- 使用另一份实现来替换原有的方法实现, 这道工序叫做"方法调配", 开发者常用此技术向原有实现中添加新功能.
- 一般来说, 只有在调试程序的时候才需要在运行时修改方法实现, 这种做法不宜滥用
第15条: 用前缀避免命名空间冲突
因为 OC 没有其他语言那种内置的命名空间机制. 鉴于此, 我们要在起名时设法避免潜在的命名冲突, 否则就很容易重名了. 如果发生命名冲突, 那么程序就会报错, 甚至会崩溃.
避免此问题的唯一方法就是变相实现命名空间: 为所有名称都加上适当的前缀.
- 注意: 使用Cocoa 创建应用程序时, 苹果宣称期保留使用所有"两个字母前缀"的去啊你. 所以自定义的前缀最好应该是三个字母的.
不仅是类名, 应用程序中的所有名称都应该加上前缀. 包括类的实现文件中所使用的纯 C函数及全局变量.
要点
- 为你的类选择一个有意义的前缀, 并在所有代码中均使用这一前缀
- 若自己开发的程序库中使用了第三方库, 则应为其中的名称加上前缀