OC中的方法调用的本质,都是转换为objc_msgSend函数的调用。
这里所说的消息机制就是objc_msgSend的执行流程。
objc_msgSend的执行流程可以分为3大阶段:
1、消息发送
1、首先判断消息接收者是否为nil,如果为nil则直接退出。所以存在使用 nil 调用方法,编译的时候也不会报错。
2、如果消息接收者不为空,通过isa指针找到消息接收者类对象,然后去查找方法(如果类对象没有找到方法,通过superclass指针找到父类继续查询),具体流程如下图:
receiver通过isa指针找到receiverClass
注意:如果是在class_rw_t中查找方法:
1、已经排序的,二分查找
2、没有排序的,遍历查找
如果消息接收者的类和所有父类中都没有找到方法实现。则进入动态方法解析阶段。
2、动态方法解析
底层实现代码:
_class_resolveMethod(cls,sel,inst);
triedResolver:是否动态解析的标记。
2.1、实例方法动态解析
方法一:
//-(void)test { // NSLog(@"%s",__func__); //} - (void)other { NSLog(@"%s", __func__); } + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(test)) { // 获取其他方法 Method method = class_getInstanceMethod(self, @selector(other)); // 动态添加test方法的实现 class_addMethod(self, sel, method_getImplementation(method), method_getTypeEncoding(method) ); // 返回YES代表有动态添加方法 return YES; } return [super resolveInstanceMethod:sel]; }
注意:
Method 可以理解为等价于struct method_t *
方法二:
//- (void)test { // NSLog(@"%s",__func__); //} void c_other(id self, SEL _cmd) { NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd)); } + (BOOL)resolveInstanceMethod:(SEL)sel { if (sel == @selector(test)) { // 动态添加test方法的实现 class_addMethod(self, sel, (IMP)c_other, "v16@0:8" ); // 返回YES代表有动态添加方法 return YES; } return [super resolveInstanceMethod:sel]; }
方法实现:
2.2、类方法动态解析
//+ (void)test { // NSLog(@"%s",__func__); //} void c_other(id self, SEL _cmd) { NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd)); } + (BOOL)resolveClassMethod:(SEL)sel { if (sel == @selector(test)) { // 注意:第一个参数是类对象【object_getClass(self)】 class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8"); return YES; } return [super resolveClassMethod:sel]; }
3、消息转发
底层实现代码:
imp = (IMP)_objc_msgforward_impcache;
开发者可以在forwardInvocation:方法中自定义任何逻辑
以上方法都有对象方法、类方法2个版本(前面可以是加号+,也可以是减号-)
解决方案:
3.1、实例方法
1、将消息转发给另一个实例对象实现
本质:在另外一个实例对象,实现一个一样的方法
- (id)forwardingTargetForSelector:(SEL)aSelector { if (aSelector == @selector(test)) { // objc_msgSend([[Cat alloc] init], aSelector) return [[Cat alloc] init]; } return [super forwardingTargetForSelector:aSelector]; }
Cat.h:
#import <Foundation/Foundation.h> @interface Cat : NSObject - (void)test; @end
Cat.m:
#import "Cat.h" @implementation Cat - (void)test { NSLog(@"%s", __func__); } @end
2、方法签名
// 方法签名:返回值类型、参数类型 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if (aSelector == @selector(test)) { return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"]; // return [[[Cat alloc] init] methodSignatureForSelector:aSelector]; } return [super methodSignatureForSelector:aSelector]; } /** * NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数 * anInvocation.target 方法调用者 * anInvocation.selector 方法名 * [anInvocation getArgument:NULL atIndex:0] */ - (void)forwardInvocation:(NSInvocation *)anInvocation { // 参数顺序:receiver、selector、other arguments // anInvocation.target == [[MJCat alloc] init] // anInvocation.selector == test: // anInvocation的参数:15 // [[[Cat alloc] init] test:15] [anInvocation invokeWithTarget:[[Cat alloc] init]]; // 返回值 int ret; [anInvocation getReturnValue:&ret]; NSLog(@"%d", ret); }
3.2、类方法
1、将消息转发给另一个实例对象实现
+ (id)forwardingTargetForSelector:(SEL)aSelector { // objc_msgSend([[Cat alloc] init], @selector(test)) // [[[Cat alloc] init] test] if (aSelector == @selector(test)) { return [[Cat alloc] init]; } return [super forwardingTargetForSelector:aSelector]; }
2、方法签名
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { if (aSelector == @selector(test)) { return [NSMethodSignature signatureWithObjCTypes:"v@:"]; } return [super methodSignatureForSelector:aSelector]; } + (void)forwardInvocation:(NSInvocation *)anInvocation { NSLog(@"1123"); }
当以上三个阶段没有实现,那就是 objc_msgSend 找不到合适的方法进行调用,会报错unrecognized selector sent to instance。
以上是从消息发送后,消息处理的全部流程。