在Objective-C中,如果向一个对象发送一条该对象无法处理的消息(对应selector不存在),会导致程序crash, 但是,在crash之前,oc的运行时系统会先经过以下两个步骤:
- Dynamic Method Resolution
- Message Forwarding
1 Dynamic Method Resolution(动态方法决议)
Objective C 提供了一种名为动态方法决议的手段,使得我们可以在运行时动态地为一个 selector 提供实现。我们只要实现 +resolveInstanceMethod: 或 +resolveClassMethod: 方法,并在其中为指定的 selector 提供实现即可(通过调用运行时函数 class_addMethod 来添加),并返回YES,运行时系统会重启一次消息的发送过程,调用动态添加的方法。例如,下面的例子:
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@implementation MyClass
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
@end
class_addMethod 方法动态的添加新的方法与对应的实现,如果调用了[MyClass resolveThisMethodDynamically],将会转到动态添加的dynamicMethodIMP 方法中。Objective-C的方法本质上是一个至少包含两个参数(id self, SEL _cmd)的C函数,这样,当重启消息发送时,就能在类中找到@selector(dynamicMethodIMP)了。而如果方法返回NO时,将会进入下一步:消息转发(Message Forwarding)
2 Message Forwarding(消息转发)
消息转发分为两步:
首先运行时系统会调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,如果这个方法中返回的不是nil或者self,运行时系统将把消息发送给返回的那个对象。
如果返回的是nil或者self,运行时系统首先会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法来获得方法签名,方法签名记录了方法的参数和返回值的信息。
如果-methodSignatureForSelector 返回的是nil, 运行时系统会抛出unrecognized selector exception,程序到这里就结束了。
3 流程图
整个流程可以用下面这张图表示
调用[[YJItem alloc] test]
,从控制台打印方法的执行顺序如下所示
resolveInstanceMethod:
forwardingTargetForSelector:
methodSignatureForSelector:
resolveInstanceMethod:
doesNotRecognizeSelector:
-[YJItem test]: unrecognized selector sent to instance 0x61800000d5c0
Appendix
Related Documentation
Objective-C Runtime Programming Guide
Revision History
时间 | 描述 |
---|---|
2016-10-14 | 博文完成 |
2016-10-17 | 添加控制台crash输出 |
Copyright
CSDN:http://blog.csdn.net/y550918116j
GitHub:https://github.com/937447974