OC底层探索 - 动态方法决议 & 消息转发

128 阅读5分钟

上一篇相关内容:OC底层探索 - 方法的底层原理

动态方法决议

一旦实现动态方法决议方法,必须给 sel 添加 imp ,否则会崩溃。

static NEVER_INLINE IMP
resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
{
    runtimeLock.assertLocked();
    ASSERT(cls->isRealized());

    runtimeLock.unlock();
    // 判断当前cls是否是元类
    if (! cls->isMetaClass()) {
        // 非元类,所以尝试  [cls resolveInstanceMethod:sel]
        // try [cls resolveInstanceMethod:sel]
        resolveInstanceMethod(inst, sel, cls);
    } 
    else {
        // 元类,所以尝试 [nonMetaClass resolveClassMethod:sel]
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        resolveClassMethod(inst, sel, cls);
        // 如果 cache中 没找到 imp 的实现
        // 尝试 [cls resolveInstanceMethod:sel]
        if (!lookUpImpOrNilTryCache(inst, sel, cls)) {
            resolveInstanceMethod(inst, sel, cls);
        }
    }
    // 可能现在有缓存了,所以再尝试查询一次
    // chances are that calling the resolver have populated the cache
    // so attempt using it
    return lookUpImpOrForwardTryCache(inst, sel, cls, behavior);
}

实例方法的动态方法决议

+ (BOOL)resolveInstanceMethod:(SEL)sel
static void resolveInstanceMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    SEL resolve_sel = @selector(resolveInstanceMethod:);

    // 容错处理,判断该元类的继承链中是否有resolveInstanceMethod方法
    // 如果自定义没实现,则会找到NSObject
    if (!lookUpImpOrNilTryCache(cls, resolve_sel, cls->ISA(/*authenticated*/true))) {
        // Resolver not implemented.
        return;
    }
    // 这里是消息发送,向 cls 发送 resolveInstanceMethod: 这个方法的消息
    // 所以我们可以在cls中实现,+(BOOL)resolveInstanceMethod:(SEL)sel 来处理实例方法找不到的情况
    // 比如给 sel 添加一个 imp 的实现
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls, resolve_sel, sel);

    // 去 cache 中查找 sel 的 imp,如果resolveInstanceMethod中有
    // 添加 imp的实现,那么 cache中就可以找到了
    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveInstanceMethod adds to self a.k.a. cls
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
    // 异常情况,打印错误信息
    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

示例

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(func2)) {
        // 基于runtime,向类对象添加一个方法
        class_addMethod(self.class, sel, class_getMethodImplementation(self.class, @selector(func4)), "v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

- (void)func4 {
    NSLog(@"func4");
}

类方法的动态方法决议

+ (BOOL)resolveClassMethod:(SEL)sel
/***********************************************************************
* resolveClassMethod
* Call +resolveClassMethod, looking for a method to be added to class cls.
* cls should be a metaclass.
* Does not check if the method already exists.
**********************************************************************/
// 这里的 cls 是元类
static void resolveClassMethod(id inst, SEL sel, Class cls)
{
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    ASSERT(cls->isMetaClass());

    // 容错处理,判断该元类的继承链中是否有resolveClassMethod方法
    // 如果自定义没实现,则会找到NSObject
    if (!lookUpImpOrNilTryCache(inst, @selector(resolveClassMethod:), cls)) {
        // Resolver not implemented.
        return;
    }

    Class nonmeta;
    {
        // 得到元类的对应的类对象,因为我们无法在元类中些代码
        // 所以 + (BOOL)resolveClassMethod:(SEL)sel 只能写在类对象中
        // 那么,我们就需要向类对象发送消息了
        mutex_locker_t lock(runtimeLock);
        nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
        // +initialize path should have realized nonmeta already
        if (!nonmeta->isRealized()) {
            _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
                        nonmeta->nameForLogging(), nonmeta);
        }
    }
    // 这里向类对象发送消息
    BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
    // 从cache中获取 imp
    // Cache the result (good or bad) so the resolver doesn't fire next time.
    // +resolveClassMethod adds to self->ISA() a.k.a. cls
    IMP imp = lookUpImpOrNilTryCache(inst, sel, cls);
    // 异常处理
    if (resolved  &&  PrintResolving) {
        if (imp) {
            _objc_inform("RESOLVE: method %c[%s %s] "
                         "dynamically resolved to %p", 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel), imp);
        }
        else {
            // Method resolver didn't add anything?
            _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
                         ", but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(), sel_getName(sel), 
                         cls->isMetaClass() ? '+' : '-', 
                         cls->nameForLogging(), sel_getName(sel));
        }
    }
}

示例

+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(func1)) {
        // 基于runtime,向元类对象添加一个方法
        class_addMethod(objc_getMetaClass("AClass"), sel, class_getMethodImplementation(objc_getMetaClass("AClass"), @selector(func3)),"v@:");
        return YES;
    }
    return [super resolveClassMethod:sel];
}

+ (void)func3 {
    NSLog(@"func3");
}

消息转发

在动态方法决议之后,如果还是没有找到方法的实现。那么就会进入到消息转发阶段。

消息转发,顾名思义就是,当类自己没有实现方法,那么可以指定一个实现了方法的类/实例来执行方法,也就是把消息转发出去。

消息转发有2种,快速消息转发 和 慢速消息转发。 系统会首先尝试 快速消息转发 , 如果 快速消息转发 没有找到,那么会进入 慢速消息转发, 如果慢速消息转发 也未找到,则会执行doesNotRecognizeSelector抛出异常。

快速消息转发 forwardingTargetForSelector


/* AClass */
@interface AClass : NSObject

- (void)func1;
+ (void)func2;

@end
@implementation AClass

- (void)func1 {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)func2 {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

/* BClass */
@interface BClass : NSObject

- (void)func1;
+ (void)func2;

@end
@implementation BClass

// 实例方法的快速消息转发
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(func1)) {
        // 返回的是实例对象
        return [AClass new];
    }
    return  nil;
}

// 类方法的快速消息转发
+ (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(func2)) {
        // 返回的是类对象
        return [AClass class];
    }
    return  nil;
}

@end


BClass *b = [BClass new];
[b func1]; // AClass -[AClass func1]
[BClass func2]; // AClass +[AClass func2]

快速消息转发 的实现可以写在父类或者它们的分类中,都能起到作用

慢速消息转发 methodSignatureForSelector

需要注意的是methodSignatureForSelector返回的是一个方法签名,需要结合 forwardInvocation 方法来处理才行。没有实现forwardInvocation 会崩溃。

/* AClass */
@interface AClass : NSObject

- (void)func1;
+ (void)func2;

@end

@implementation AClass

- (void)func1 {
    NSLog(@"%@ %s", [self class], __func__);
}

+ (void)func2 {
    NSLog(@"%@ %s", [self class], __func__);
}

@end

/* BClass */
@interface BClass : NSObject

- (void)func1;
+ (void)func2;

@end

@implementation BClass

// 类方法的慢速消息转发,这里返回一个有效的方法签名
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

+ (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"什么都不做");
}

// 实例方法的慢速消息转发,这里返回一个有效的方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    AClass *a = [AClass new];
    if ([self respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:self];
    }else if ([a respondsToSelector:anInvocation.selector]) {
        [anInvocation invokeWithTarget:a];
    }else {
        NSLog(@"该方法未处理");
    }
}

@end


BClass *b = [BClass new];
[b func1]; // AClass -[AClass func1]
[BClass func2]; // 什么都不做

可见,慢速消息转发 中 forwardInvocation 使用非常灵活,只要有实现就不会崩溃,可以什么都不做,也以自己处理,也可以转发给别人。

慢速消息转发中方法调用顺序是:先methodSignatureForSelector, 后 forwardInvocation