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
。
以上是从消息发送后,消息处理的全部流程。