iOS 消息转发

当我们调用一个不存在的方法时,就会报:unrecognized selector sent to instance **。消息接收者找不到对应的selector,这样就启动了消息转发机制,我们可以通过代码在消息转发的过程中告诉对象应该如何处理未知的消息,默认抛出上面的异常。

1、对象在收到未知消息后,首先会调用 + (BOOL)resolveInstanceMethod:(SEL)sel 或者 + (BOOL)resolveClassMethod:(SEL)sel ,询问是否有动态方法来进行处理

// 调用
Person *p1 = [[Person alloc] init];
[p1 performSelector:@selector(speak)];
[Person performSelector:@selector(eat)];
// Person.m
#import <objc/runtime.h>
void speak(id self,SEL _cmd) {
    NSLog(@"Now I can speak");
}
void eat(id self,SEL _cmd) {
    NSLog(@"eat......");
}
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"resolveInstanceMethod:%@",NSStringFromSelector(sel));
    if (sel == @selector(speak)) {
        class_addMethod([self class], sel, (IMP)speak, "vvv");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
    NSLog(@"resolveClassMethod:%@",NSStringFromSelector(sel));
    if (sel == @selector(eat)) {
        class_addMethod([self superclass], sel, (IMP)eat, "vvv");
        return YES;
    }
    return [super resolveClassMethod:sel];
}
添加动态方法

 当Person收到未知消息的时候,如果是实例方法会调用resolveInstanceMethod方法、类方法会调用resolveClassMethod方法,然后通过class_addMethod方法动态添加实现方法来解决未知消息,此时消息转发过程提前结束

2、如果第一步返回的是NO,也就是没有动态新增实现方法就会调用第二步。使用 - (id)forwardingTargetForSelector:(SEL)aSelector 让别的类帮忙处理。

//  调用
//  ((void (*)(id, SEL))objc_msgSend)(p1, @selector(fly));
[p1 performSelector:@selector(fly)];
// Person.m
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"forwardingTargetForSelector:  %@", NSStringFromSelector(aSelector));
    Bird *bird = [[Bird alloc] init];
    if ([bird respondsToSelector: aSelector]) {
        return bird;
    }
    return [super forwardingTargetForSelector: aSelector];
}
// Bird.m
- (void)fly
{
    NSLog(@"I can fly");
}
让其他类处理这条消息

3、如果前两种方法都不能处理未知消息,则使用下面方法

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"forwardInvocation: %@", NSStringFromSelector([anInvocation selector]));
    if ([anInvocation selector] == @selector(drink)) {
        Bird *b1 = [[Bird alloc] init];
        [anInvocation invokeWithTarget:b1];
    }
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    NSLog(@"method signature for selector: %@", NSStringFromSelector(aSelector));
    if(aSelector == @selector(drink)) {
        return [NSMethodSignature signatureWithObjCTypes:"aaaa"];
    }
    return [super methodSignatureForSelector:aSelector];
}
最后一步

如果最后消息还是未能处理,还会调用 - (void)doesNotRecognizeSelector:(SEL)aSelector ,可以在这个方法里做些处理,防止crash

未知消息处理顺序

原文地址:https://www.cnblogs.com/chenyanliang/p/9299331.html