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部曲:

如果以上这3个流程中的任何一个有做了处理,person调用sayLove都不会奔溃

消息转发.png

now,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);
}
上一篇下一篇

猜你喜欢

热点阅读