消息传递
[object doSomething]被编译器转化为
id objc_msgSend ( id self, SEL op, ... );
在“消息传递”过程中,objc_msgSend
的动作比较清晰:首先在 Class 中的缓存查找 IMP (没缓存则初始化缓存),如果没找到,则向父类的 Class 查找。如果一直查找到根类仍旧没有实现,则用objc_msgForward
函数指针代替 IMP 。最后,执行这个 IMP 。
objc_msgSend 相关函数伪代码:
id objc_msgSend(id self, SEL op, ...) {
if (!self) return nil;
IMP imp = class_getMethodImplementation(self->isa, SEL op);
imp(self, op, ...); //调用这个函数,伪代码...
}
//查找IMP
IMP class_getMethodImplementation(Class cls, SEL sel) {
if (!cls || !sel) return nil;
IMP imp = lookUpImpOrNil(cls, sel);
if (!imp) return _objc_msgForward; //_objc_msgForward 用于消息转发
return imp;
}
IMP lookUpImpOrNil(Class cls, SEL sel) {
if (!cls->initialize()) {
_class_initialize(cls);
}
Class curClass = cls;
IMP imp = nil;
do { //先查缓存,缓存没有时重建,仍旧没有则向父类查询
if (!curClass) break;
if (!curClass->cache) fill_cache(cls, curClass);
imp = cache_getImp(curClass, sel);
if (imp) break;
} while (curClass = curClass->superclass);
return imp;
}
class_getMethodImplementation
寻找IMP过程:
- 先从当前class的cache方法列表(cache methodLists)里去找
- 找到了,跳到对应函数实现
- 没找到,就从class的方法列表(methodLists)里找
- 还找不到,就到super class的方法列表里找,直到找到基类(NSObject)为止
- 最后再找不到,就会调用_objc_msgForward函数,进行消息转发流程。
消息转发
_objc_msgForward
是一个函数指针(和 IMP 的类型一样),是用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward
会尝试做消息转发。
1.调用resolveInstanceMethod:方法 (或 resolveClassMethod:)。允许用户在此时为该 Class 动态添加实现。如果有实现了,则调用并返回YES,那么重新开始objc_msgSend流程。这一次对象会响应这个选择器,一般是因为它已经调用过class_addMethod。如果仍没实现,继续下面的动作。
2.调用forwardingTargetForSelector:方法,尝试找到一个能响应该消息的对象。如果获取到,则直接把消息转发给它,返回非 nil 对象。否则返回 nil ,继续下面的动作。注意,这里不要返回 self ,否则会形成死循环。
3.调用methodSignatureForSelector:方法,尝试获得一个方法签名。如果获取不到,则直接调用doesNotRecognizeSelector抛出异常。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation:。
4.调用forwardInvocation:方法,将第3步获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非ni。
5.调用doesNotRecognizeSelector: ,默认的实现是抛出异常。如果第3步没能获得一个方法签名,执行该步骤。
上面前4个方法均是模板方法,开发者可以override,由 runtime 来调用。最常见的实现消息转发:就是重写方法3和4,吞掉一个消息或者代理给其他对象都是没问题的
也就是说_objc_msgForward在进行消息转发的过程中会涉及以下这几个方法:
resolveInstanceMethod:方法 (或 resolveClassMethod:)。
forwardingTargetForSelector:方法
methodSignatureForSelector:方法
forwardInvocation:方法
-
doesNotRecognizeSelector: 方法
步骤1、2、3都是在没有实现msg方法的情况下,runtime为我们提供的补救的机会。
第一种:resolveInstanceMethod:
// c形式
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(say:)) {
class_addMethod([self class], sel, (IMP)say, "v@:*");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void say(id self, SEL _cmd, NSString *str) {
NSLog(@"Person say:%@", str);
}
//OC形式
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(say:)) {
class_addMethod([self class], sel, class_getMethodImplementation([self class], @selector(sayMethodIMP:)), "v@:*");
return YES;
}
return [super resolveInstanceMethod:sel];
}
- (void)sayMethodIMP:(NSString *)str {
NSLog(@"Person say:%@", str);
}
第二种:forwardingTargetForSelector:这里增加了Mobile类来相应say:消息。
//Mobile.h
#import
@interface Mobile : NSObject
- (void)say:(NSString*)world;
@end
//Mobile.m
#import "Mobile.h"
@implementation Mobile
- (void)say:(NSString*)world {
NSLog(@"Mobile say:%@", world);
}
@end
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(say:)) {
return [Mobile new];
}
return [super forwardingTargetForSelector:aSelector];
}
第三种:forwardInvocation:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(say:)) {
return [NSMethodSignature signatureWithObjCTypes:"V@:*"];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL selector = [anInvocation selector];
Mobile *mobile = [Mobile new];
if ([mobile respondsToSelector:selector]) {
[anInvocation invokeWithTarget:mobile];
}
}