首先理解几个名词:
1.SEL :Objective-C 在编译时,会根据方法的名字生成一个用来区分这个方法的唯一的一个ID,本质上就是一个字符串。只要方法名称相同,那么它们的ID就是相同的。
2.Method:
3.IMP
方法调用查找流程
检查 selector 是否需要忽略
检查 target 是否为 nil,如果是 nil 就直接 cleanup,然后 return
在 target 的 Class 中根据 selector 去找 IMP
寻找 IMP 的过程[2]:
在当前 class 的方法缓存里寻找(cache methodLists)
找到了跳到对应的方法实现,没找到继续往下执行
从当前 class 的 方法列表里查找(methodLists),找到了添加到缓存列表里,然后跳转到对应的方法实现;没找到继续往下执行
从 superClass 的缓存列表和方法列表里查找,直到找到基类为止
以上步骤还找不到 IMP,则进入消息动态处理和消息转发流程
方法转发流程
1.消息动态处理
voiddynamicMethodIMP(idself, SEL _cmd)
{// implementation ....}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"sel is %@",NSStringFromSelector(sel));
if(sel ==@selector(setName:))
{
class_addMethod([selfclass],sel,(IMP)dynamicMethodIMP,"v@:");
returnYES;
}
return [superresolveInstanceMethod:sel];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
//如果代理对象能处理,则转接给代理对象if([proxyObj respondsToSelector:aSelector]) {returnproxyObj;
}
//不能处理进入转发流程returnnil;
}
2.消息转发
<pre>
- (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector{NSMethodSignature*sig = [BBMessageForwardProxy instanceMethodSignatureForSelector:@selector(bb_dealNotRecognizedMessage:)];returnsig;}- (void)forwardInvocation:(NSInvocation*)anInvocation{NSString*debugInfo = [NSStringstringWithFormat:@"[debug]unRecognizedMessage:[%@] sent to [%@]",NSStringFromSelector(anInvocation.selector),NSStringFromClass([selfclass])];//重定向方法[anInvocation setSelector:@selector(bb_dealNotRecognizedMessage:)];//传递调用信息[anInvocation setArgument:&debugInfo atIndex:2];//BBMessageForwardProxy对象接收转发的消息并打印调用信息[anInvocation invokeWithTarget:[BBMessageForwardProxy new]];}
</pre>
如果 methodSignatureForSelector 返回的NSMethodSignature 是 nil 的话不会继续执行 forwardInvocation,转发流程终止,抛出无法处理的异常。
Class和MetaClass
当我们对一个实例发送消息时(-开头的方法),会在该 instance 对应的类的 methodLists 里查找。
当我们对一个类发送消息时(+开头的方法),会在该类的 MetaClass 的 methodLists 里查找。
load和initialize
我们知道了 load 是在被添加到 runtime 时开始执行,父类最先执行,然后是子类,最后是 Category。又因为是直接获取函数指针来执行,不会像 objc_msgSend 一样会有方法查找的过程。
initialize 最终是通过 objc_msgSend 来执行的,objc_msgSend 会执行一系列方法查找,并且 Category 的方法会覆盖类中的方法。