Runtime消息发送
OC代码
Person *obj = [Person new];
[obj eat];
编译后
objc_msgSend(obj, @selector(eat));
objc_msgSend发送消息流程
- 通过
isa指针找到所属类 - 在所属类缓存列表里面查找,如果没有则下一步
- 所属类的方法列表查找,找到与选择器方法名称相符的方法,跳到实现代码
- 找不到, 就沿着继承体系继续向上查找
- 如果能找到与选择器名称相符的方法, 就跳至其实现代码
- 找不到,执行消息转发
Runtime消息转发
消息转发流程
- 动态解析
- 快速转发
- 慢速转发
1.消息动态解析
如果是实例方法没有实现,动态解析调用
+(BOOL)resolveInstanceMethod:(SEL)sel
如果是类方法没有实现,动态解析调用
+(BOOL)resolveClassMethod:(SEL)sel
实例方法为例,代码如下:
+ (BOOL)resolveInstanceMethod:(SEL)sel {
BOOL isEqualSEL = sel_isEqual(sel, @selector(btnAction:));
if (isEqualSEL == YES) {
//把IMP指针加入到 receiver 方法列表
class_addMethod([self class], @selector(btnAction:), (IMP)addMessage, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
//IMP实现
void addMessage(id self, SEL _cmd) {
//do something
}
2.快速转发
快速转发,调用系统Api
-(id)forwardingTargetForSelector:(SEL)aSelector;
返回一个新的类对象,新的类对象实现了当前类对象没有实现的方法
- (id)forwardingTargetForSelector:(SEL)aSelector {
Custom *obj = [Custom new];
if (sel_isEqual(aSelector, @selector(btnAction))) {
return obj;
}
return nil;
}
@interface Custom : NSObject
@end
@implementation Custom
- (void)btnAction {
// do something
}
3.慢速转发
慢速转发使用两个对象 NSInvocation和 NSMethodSignature
主要用到如下Api
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;//在协议中实现,返回相关的方法签名
- (void)forwardInvocation:(NSInvocation *)anInvocation;//进行消息的委派
- (void)invokeWithTarget:(id)anObject;//唤醒接受者的同名方法
- (void)doesNotRecognizeSelector:(SEL)aSelector;/
相关代码如下:
//重定向的实现类
@interface Custom : NSObject
@end
@implementation Custom
- (void)btnAction {
// do something
}
@end
//慢速转发实现
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (sel_isEqual(aSelector, @selector(btnAction))) {
//创建新的方法签名
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:"];
return methodSignature;
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"do method : %@", NSStringFromSelector(_cmd));
FarwardTarget *target = [[FarwardTarget alloc] init];
if ([target respondsToSelector:anInvocation.selector]) {
//唤醒接受者的同名方法
[anInvocation invokeWithTarget:target];
}else {
[self doesNotRecognizeSelector:anInvocation.selector];
}
}
总结
- 动态解析 : 适用于将原来的类中的方法替换掉或者延迟加载。
- 快速转发 : 可以将消息处理转发给其他对象,使用范围更广,不只是限于原来的对象。
- 慢速转发 : 跟快转发一样可以消息转发,但它能通过NSInvocation对象获取更多消息发送的信息,例如:target、selector、arguments和返回值等信息。