消息转发是什么
在oc中调用方法就是发送消息(msgSend),如果给一个实例对象(Instance)发送一个未定义的消息,肯定会crash
在VC里
Person *p = [Person new];
[p run];
在Person类中
run方法没有实现
就会报这个错误
unrecognized selector sent to instance 0x600000008310
如果在运行时调用动态决议方法resolveInstanceMethod
或者resolveClassMethod
,决议失败,就是找不到,那么就会接着去调用methodSignatureForSelector
,然后再调用forwardInvocation
。如果对象没有重写这两个方法,就代表不支持方法转发调用,那么就会调用父类NSObject方法,NSObject父类方法forwardInvocation 中如下所示,所以导致异常,crash
- (void) forwardInvocation: (NSInvocation*)anInvocation
{
[self doesNotRecognizeSelector:[anInvocation selector]];
return;
}
- (void) doesNotRecognizeSelector: (SEL)aSelector
{
[NSException raise: NSInvalidArgumentException
format: @"%s does not recognize %s",
object_get_class_name(self), sel_get_name(aSelector)];
}
下面是实现消息转发
//方法签名 第三种
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSString *sel = NSStringFromSelector(aSelector);
//手动生成签名
if ([sel isEqualToString:@"run"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}else {
return [super methodSignatureForSelector:aSelector];
}
}
//拿到方法签名配发消息
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"-----%@-----",anInvocation);
//取得消息
SEL selector = [anInvocation selector];
//�转发
SomePerson *someP = [SomePerson new];
if ([someP respondsToSelector:selector]) {
//调用对象,进行转发
[anInvocation invokeWithTarget:someP];
} else {
return [super forwardInvocation:anInvocation];
}
}
消息转发分为两步
首先系统会调用- (id)forwardingTargetForSelector:(SEL)aSelector
如果这个方法的返回值不是nil或者self,运行时系统会把消息发送给返回的哪个对象
如果返回的是nil或者self,运行时系统首先会调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
来获得方法签名
如果-methodSignatureForSelector
返回的是nil,运行时系统会抛出unrecognized selector exception
,程序到这里就结束了
动态方法决议
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)
2016101401.png
网友评论