美文网首页程序员
是否能够响应方法:respondsToSelector的实现

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

作者: Even会编程 | 来源:发表于2020-12-24 08:57 被阅读0次

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

    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)
        {
            // <rdar://problem/46904873> 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的实现

        本文链接:https://www.haomeiwen.com/subject/jfipnktx.html