11.理解objc_msgSend的作用
id returnValue = [someObject messageName: parameter];
其中someObject是“接收者”,messageName叫做“选择子”,选择子与参数合起来称为“消息”。
void objc_msgSend(id self, SEL cmd, ...)
第一个参数代表接收者,第二个参数代表选择子(SEL是选择子的类型),后续就是消息中的参数
即:id returnValue = objc_msgSend(someObject,@selector(messageName:),parameter)
objc_msgSend函数会根据接收者与选择子的类型来调用适当的方法,为了完成此操作,该方法需要在接收者所属的类中搜寻其“方法列表”,如果找不到相符的方法,会执行“消息转发”操作。
消息返回的是结构体:objc_msgSend_stret
消息返回的是浮点型:objc_msgSend_fpret
要给超类发消息:objc_msgSendSuper
12.理解消息转发机制
当对象接收到无法解读的消息后,就会启动“消息转发”机制。
消息转发分为两大阶段。第一个阶段先征询接收者,所属的类,看其是否能动态添加方法,以处理这个“未知的选择子”,这就叫做“动态方法解析”。第二阶段涉及“完整的消息转发机制”,如果运行期系统已经把第一阶段执行完了,那么接收者自己就无法再以动态新增方法的手段来响应包含该选择子的消息了。此时,运行期系统会请求接收者以其他手段来处理与消息相关的方法调用。这又细分为两小步。首先,请接收者看看有没有其他对象能处理有这条消息,若有则运行期系统会把消息转给那个对象,若没有“备援的接收者”,则启动完整的消息转发机制,运行期系统会把与消息有关的全部细节都封装到NSInvocation对象中,再给接收者最后一次机会,令其设法解决当前还未处理的消息。
动态方法解析
对象在接收到无法解读的消息后,首先会调用所属类的下列类方法:
+(BOOL)resolveInstanceMethod:(SEL)selector
使用该方法的前提是:相关方法的实现代码已经写好,只等运行的时候动态插在类里面就可以了。
备援接收者
当前接收者还有第二次机会能处理未知的选择子,在这一步中,运行期系统会问它:能否把这条消息转给其他接收者来处理,该步骤对应的方法如下:
-(id)forwordingTargetForSelector:(SEL)selector
若当前接收者能找到备援对象,则将其返回,若找不到就返回nil。
注意:我们无法操作经由这一步所转发的消息,若是想在发送给备援接收者之前先修改消息内容,那就得通过完整的消息转发机制来做了。
完整的消息转发
首先创建NSInvocation对象,把与尚未处理的那条消息有关的全部细节都封装到其中。此对象包括选择子、目标以及参数。在触发NSInvocation对象时,“消息派发系统”会亲自把消息指派给目标对象,该步骤对应的方法如下:
-(void)forwardInvocation:(NSInvocation *)invocation
比较有效的实现方式:在触发消息前,先以某种方式改变消息内容,比如追加另一个参数,或是改换选择子等等。
实现此方法时,若发现某调用操作不应由本类处理,则需调用超类的同名方法。这样的话,集成体系中的每个类都有机会处理此调用请求,知道NSObject。如果最后调用了NSObject类的方法,那么该方法还会继而调用“doesNotRecognizeSelector”以抛出异常,此异常表明选择子最终未能得到处理。
消息转发全流程
例子代码如下:
13.用“方法调配技术”(method swizzling)调试“黑盒方法”
“方法调配”:在运行期,可以向类中新增或替换选择子所对应的方法实现。
类的方法列表会把选择子的名称映射到相关的方法实现上,使得“动态消息派发系统”能够据此找到应该调用的方法。这些方法均以函数指针的形式来表示,这种指针叫做IMP,原型如下:
id (*IMP)(id, SEL, ...)
如何交换两个方法的实现
- (NSString*)eoc_myLowercaseString{
NSString*lowercase = [self eoc_myLowercaseString];
NSLog(@"%@=>%@",self,lowercase);
return lowercase;
}
Method originalMethod =class_getInstanceMethod([NSString class],@selector(lowercaseString));
Method swappedMethod =class_getInstanceMethod([NSString class],@selector(eoc_myLowercaseString));
method_exchangeImplementations(originalMethod, swappedMethod);
开发者常用此技术向原有实现中添加新功能,一般来说只有调试程序时才需要在运行期修改方法实现,这种做法不适宜滥用。
14.理解“类对象”的用意
每个实例都有一个指向class对象的指针(isa指针),用以表明其类型,而这些Class对象则构成了类的继承体系。
如果对象类型无法在编译期确定,那么就一个使用类型查询方法来探知
isKindOfClass判断出对象是否为某类或其派生类的实例
isMemberOfClass判断出对象是否为某个特定类