OC消息转发3部曲
2020-06-14 本文已影响0人
三国韩信
@interface LGPerson : NSObject
-(void)sayNB;
@end
@implementation LGPerson
-(void)sayNB{
NSLog(@"%s",__func__);
}
@interface LGTeacher : NSObject
-(void)sayHello;
@end
@implementation LGTeacher
-(void)sayHello{
NSLog(@"+++++++++++%s",__func__);
}
@end
这里定义了2个类,LGPerson和LGTeacher。
然后在main函数里来调用他们
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
LGPerson* p = [[LGPerson alloc]init];
[p performSelector:@selector(sayLove)];
//[p performSelector:@selector(sayHello)];
//[p performSelector:@selector(sayLoving)];
}
return 0;
}
很明显LGPerson里没有sayLove这方法,那么当person对象去调用这个方法的时候,正常情况下是会奔溃的,会出现一个oc很经典的错误——方法找不到
-[LGPerson sayLove]: unrecognized selector sent to instance 0x10050ac90'
那么出现这种情况,开发者该如何预防呢,答案是消息转发3部曲:
- 动态方法决议 +resolveInstanceMethod或+resolveClassMethod
- 消息转发1 -forwardingTargetForSelector
- 消息转发2 -methodSignatureForSelector与-forwardInvocation
如果以上这3个流程中的任何一个有做了处理,person调用sayLove都不会奔溃
消息转发.pngnow,show code:
/*1.动态方法决议:通过对sayLove处理,把另一个对象LGTeacher的sayHello方法的实现赋值给sayLove,
那么调用sayLove就相当于调用了LGTeacher的sayHello方法.*/
+(BOOL)resolveInstanceMethod:(SEL)sel{
if ([NSStringFromSelector(sel) isEqualToString:@"sayLove"]) {
Method m = class_getInstanceMethod(objc_getClass([@"LGTeacher" UTF8String]), @selector(sayHello));
const char * types = method_getTypeEncoding(m);
IMP newImp = class_getMethodImplementation(objc_getClass([@"LGTeacher" UTF8String]), @selector(sayHello));
class_addMethod(self, sel, newImp, types);
return YES;
}
return [super resolveInstanceMethod:sel];
}
/*
通过对sayLove处理,把另一个对象LGTeacher返回出去,那么系统就会去LGTeacher里找一个叫sayLove的方法,
如果能找到就调用它,如果返回出去的对象(LGTeacher)也没有sayLove这个方法,那么一样会报错崩溃的。
*/
-(id)forwardingTargetForSelector:(SEL)aSelector{
if ([NSStringFromSelector(aSelector) isEqualToString:@"sayLove"]) {
return [LGTeacher alloc];
}
return [super forwardingTargetForSelector:aSelector];;
}
/*
最后就是消息转发最底层的事务转发了,把发送sayLove消息这个事务转发给Invocation去处理,
如果能Invocation知道某个对象能处理就转发给它,如果没有对象能处理,则丢弃这个事务。
即不会发生任何操作,也不会报错崩溃。
相当于[p performSelector:@selector(sayLove)]这句代码不会报错,但也啥都没发生,相当与没写这句代码。
*/
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if ([NSStringFromSelector(aSelector) isEqualToString:@"sayLove"]) {
//NSMethodSignature * sign = [NSMethodSignature methodSignatureForSelector:aSelector];
return [NSMethodSignature signatureWithObjCTypes:"v@:"];;
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s ",__func__);
if (anInvocation.selector == @selector(sayLoving)) {
if ([[LGTeacher alloc] respondsToSelector:anInvocation.selector]){
[anInvocation invokeWithTarget:[LGTeacher alloc]];
}
}
}
注:最好的方式是把这些方法都抽取到NSObject的分类中去实现。
当我们没有做任何处理的时候,系统的forwardInvocation则会去调用doesNotRecognizeSelector函数,在doesNotRecognizeSelector中就会输出unrecognized selector sent to instance那个找不到方法的报错。
- (void)forwardInvocation:(NSInvocation *)invocation {
[self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
}
- (void)doesNotRecognizeSelector:(SEL)sel {
_objc_fatal("-[%s %s]: unrecognized selector sent to instance %p",
object_getClassName(self), sel_getName(sel), self);
}