是否能够响应方法:respondsToSelector的实现

首先附上常见类和类型的定义:

typedef struct objc_class *Class;

struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

typedef struct objc_object *id;

//SEL 可以将其理解为方法的 ID. 结构如下:
typedef struct objc_selector *SEL;
// IMP 可以理解为函数指针,指向了最终的实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

    Class _Nullable super_class OBJC2_UNAVAILABLE;
    const char * _Nonnull name  OBJC2_UNAVAILABLE;
    long version                OBJC2_UNAVAILABLE;
    long info                   OBJC2_UNAVAILABLE;
    long instance_size          OBJC2_UNAVAILABLE;
    // 成员变量地址列表
    struct objc_ivar_list * _Nullable ivars     OBJC2_UNAVAILABLE;
    // 方法地址列表
    struct objc_method_list * _Nullable * _Nullable methodLists     OBJC2_UNAVAILABLE;
    // 缓存最近使用的方法地址,以避免多次在方法地址列表中查询,提升效率
    struct objc_cache * _Nonnull cache      OBJC2_UNAVAILABLE;
    // 遵循的协议列表
    struct objc_protocol_list * _Nullable protocols     OBJC2_UNAVAILABLE;
} OBJC2_UNAVAILABLE;

NSObject.mm中的该方法实现如下:

// 传入的参数为SEL对象
- (BOOL)respondsToSelector:(SEL)sel {
    // 参数分别为:当前对象,SEL对象,当前对象的class
    return class_respondsToSelector_inst(self, sel, [self class]);
}

class_respondsToSelector_inst方法的实现如下:

NEVER_INLINE BOOL 
class_respondsToSelector_inst(id inst, SEL sel, Class cls)
{
    // 检测sel是否为nil、cls是否为nil、执行lookUpImpOrNil方法判断能够找到实现方法
    return sel && cls && lookUpImpOrNil(inst, sel, cls, LOOKUP_RESOLVER);
}

关于枚举数据LOOKUP_RESOLVER的定义如下:

/* method lookup */
enum {
    LOOKUP_INITIALIZE = 1, 
    LOOKUP_RESOLVER = 2,
    LOOKUP_CACHE = 4, 
    LOOKUP_NIL = 8, 
};

lookUpImpOrNil方法的实现如下:

// behavior具备默认值0
static inline IMP
lookUpImpOrNil(id obj, SEL sel, Class cls, int behavior = 0)
{
    // 将传入的behavior和LOOKUP_CACHE、LOOKUP_NIL进行或操作
    // 代表会同时检索这几种类型的SEL
    return lookUpImpOrForward(obj, sel, cls, behavior | LOOKUP_CACHE | LOOKUP_NIL);
}

lookUpImpOrForward实现如下:

IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
{
    // 供外部使用的imps
    const IMP forward_imp = (IMP)_objc_msgForward_impcache;
    IMP imp = nil;  
    Class curClass;
    
    // 不加锁的查找cache
    runtimeLock.assertUnlocked();
    if (fastpath(behavior & LOOKUP_CACHE)) {
        imp = cache_getImp(cls, sel);
        if (imp) goto done_nolock;
    }

    // 上锁,开始搜索
    runtimeLock.lock();
    
    // 检测该类是否合法,此检查在流程启动期间非常昂贵
    checkIsKnownClass(cls);

    // swift环境下类初始化检测
    if (slowpath(!cls->isRealized())) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock可能已被删除,但现在再次被锁定
    }
    
    // 确保该类已被初始化,如果没有就调用类方法+initialize,这里也说明了为什么OC的类会在
    // 第一次接收消息后调用+initialize进行初始化,相反的,如果想要代码在类注册runtime的
    // 时候就运行,可以将代码写在+load方法里
    if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        // runtimeLock可能已被删除,但现在再次被锁定
    }

    // 对消息查找和填充cache加锁,由于填充cache是写操作,所以需要对其加锁
    // 以免加入了category之后的cache被旧的cache冲掉,导致category失效。
    runtimeLock.assertLocked();
    curClass = cls;

    // 在该类及其父类中查找该SEL
    for (unsigned attempts = unreasonableClassCount();;) {
        // 当前类的方法列表
        Method meth = getMethodNoSuper_nolock(curClass, sel);
        if (meth) {
            imp = meth->imp;
            goto done;
        }
        
        // 乐观缓存查找——从缓存方法列表中快速查找
        if (slowpath((curClass = curClass->superclass) == nil)) {
            imp = forward_imp;
            break;
        }

        // 如果超类链中存在循环,则暂停
        if (slowpath(--attempts == 0)) {
            _objc_fatal("Memory corruption in class list.");
        }

        // 父类缓存的方法
        imp = cache_getImp(curClass, sel);
        if (slowpath(imp == forward_imp)) {
            break;
        }
        if (fastpath(imp)) {
            // 在超类中找到该方法。将其缓存在此类中
            goto done;
        }
    }

    // 找不到实现,尝试一次方法解析
    if (slowpath(behavior & LOOKUP_RESOLVER)) {
        behavior ^= LOOKUP_RESOLVER;
        return resolveMethod_locked(inst, sel, cls, behavior);
    }

 done:
    log_and_fill_cache(cls, imp, sel, inst, curClass);
    // 解锁
    runtimeLock.unlock();
 done_nolock:
    if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
        return nil;
    }
    // 返回查找到的imp
    return imp;
}

关于以上代码中出现的函数及宏定义标示符注解如下:

#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))

static Class
realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
{
    return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
}

static Class
realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
{
    lock.assertLocked();

    if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
        // Non-Swift class. Realize it now with the lock still held.
        // fixme wrong in the future for objc subclasses of swift classes
        realizeClassWithoutSwift(cls, nil);
        if (!leaveLocked) lock.unlock();
    } else {
        // Swift class. We need to drop locks and call the Swift
        // runtime to initialize it.
        lock.unlock();
        cls = realizeSwiftClass(cls);
        ASSERT(cls->isRealized());    // callback must have provoked realization
        if (leaveLocked) lock.lock();
    }

    return cls;
}

static unsigned unreasonableClassCount()
{
    runtimeLock.assertLocked();

    int base = NXCountMapTable(gdb_objc_realized_classes) +
    getPreoptimizedClassUnreasonableCount();

    // Provide lots of slack here. Some iterations touch metaclasses too.
    // Some iterations backtrack (like realized class iteration).
    // We don't need an efficient bound, merely one that prevents spins.
    return (base + 1) * 16;
}

static method_t *
getMethodNoSuper_nolock(Class cls, SEL sel)
{
    runtimeLock.assertLocked();

    ASSERT(cls->isRealized());
    // fixme nil cls? 
    // fixme nil sel?

    auto const methods = cls->data()->methods();
    for (auto mlists = methods.beginLists(),
              end = methods.endLists();
         mlists != end;
         ++mlists)
    {
        //  getMethodNoSuper_nolock is the hottest
        // caller of search_method_list, inlining it turns
        // getMethodNoSuper_nolock into a frame-less function and eliminates
        // any store from this codepath.
        method_t *m = search_method_list_inline(*mlists, sel);
        if (m) return m;
    }

    return nil;
}
转载时请注明“来自-EvenZhu”

你可能感兴趣的:(是否能够响应方法:respondsToSelector的实现)