美文网首页
objc_method(转)

objc_method(转)

作者: Winny_园球 | 来源:发表于2018-04-26 13:58 被阅读0次

    文章地址:http://blog.csdn.net/uxyheaven
    http://blog.csdn.net/uxyheaven/article/details/38120335

    进入正题:

    本篇文章将探究一下objc里的关于方法的函数是如何实现的

    首先看下方法的定义,方法是一个objc_method结构体

    objc_method

    objc_method是类的一个方法的描述

    定义如下

    typedef struct objc_method *Method;  
      
    struct objc_method {  
        SEL method_name;        // 方法名称  
        charchar *method_typesE;    // 参数和返回类型的描述字串  
        IMP method_imp;         // 方法的具体的实现的指针  
    }  
    

    方法class_getInstanceMethod(Class aClass,SEL aSelector)

    返回ACLASS的名为aSelector方法的

    定义如下

    Method class_getInstanceMethod(Class cls, SEL sel)  
    {  
        if (!cls  ||  !sel) return NULL;  
      
        return look_up_method(cls, sel, YES/*cache*/, YES/*resolver*/);  
    }  
      
    static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver)  
    {  
        Method meth = NULL;  
        // 1. 找缓存,有过有就返回  
        if (withCache) {  
            meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal);  
            if (meth == (Method)1) {  
                // Cache contains forward:: . Stop searching.  
                return NULL;  
            }  
        }  
        // 2. 找自身  
        if (!meth) meth = _class_getMethod(cls, sel);  
      
        // 3. 找转发  
        if (!meth  &&  withResolver) meth = _class_resolveMethod(cls, sel);  
      
        return meth;  
    }  
    

    IMP class_getMethodImplementation(Class cls,SEL名称)

    返回CLS的名称的方法调用地址

    定义如下

    IMP class_getMethodImplementation(Class cls, SEL sel)  
    {  
        IMP imp;  
      
        if (!cls  ||  !sel) return NULL;  
      
        imp = lookUpMethod(cls, sel, YES/*initialize*/, YES/*cache*/);  
      
        // Translate forwarding function to C-callable external version  
        if (imp == (IMP)&_objc_msgForward_internal) {  
            return (IMP)&_objc_msgForward;  
        }  
      
        return imp;  
    }  
      
    PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel,   
                                    BOOL initialize, BOOL cache)  
    {  
        Class curClass;  
        IMP methodPC = NULL;  
        Method meth;  
        BOOL triedResolver = NO;  
      
        // Optimistic cache lookup  
        // 1. 先找下缓存  
        if (cache) {  
            methodPC = _cache_getImp(cls, sel);  
            if (methodPC) return methodPC;      
        }  
      
        // realize, +initialize, and any special early exit  
        // 2. 初始化下这个类,为接下来做准备  
        methodPC = prepareForMethodLookup(cls, sel, initialize);  
        if (methodPC) return methodPC;  
      
      
        // The lock is held to make method-lookup + cache-fill atomic   
        // with respect to method addition. Otherwise, a category could   
        // be added but ignored indefinitely because the cache was re-filled   
        // with the old value after the cache flush on behalf of the category.  
     retry:  
        lockForMethodLookup();  
      
        // Ignore GC selectors  
        if (ignoreSelector(sel)) {  
            methodPC = _cache_addIgnoredEntry(cls, sel);  
            goto done;  
        }  
      
        // Try this class's cache.  
        // 3. 先试着找缓存  
        methodPC = _cache_getImp(cls, sel);  
        if (methodPC) goto done;  
      
        // Try this class's method lists.  
        // 4. 找自己的method列表  
        meth = _class_getMethodNoSuper_nolock(cls, sel);  
        if (meth) {  
            log_and_fill_cache(cls, cls, meth, sel);  
            methodPC = method_getImplementation(meth);  
            goto done;  
        }  
      
        // Try superclass caches and method lists.  
        // 5. 找父类的缓存和method列表  
        curClass = cls;  
        while ((curClass = _class_getSuperclass(curClass))) {  
            // Superclass cache.  
            meth = _cache_getMethod(curClass, sel, &_objc_msgForward_internal);  
            if (meth) {  
                if (meth != (Method)1) {  
                    // Found the method in a superclass. Cache it in this class.  
                    log_and_fill_cache(cls, curClass, meth, sel);  
                    methodPC = method_getImplementation(meth);  
                    goto done;  
                }  
                else {  
                    // Found a forward:: entry in a superclass.  
                    // Stop searching, but don't cache yet; call method   
                    // resolver for this class first.  
                    break;  
                }  
            }  
      
            // Superclass method list.  
            meth = _class_getMethodNoSuper_nolock(curClass, sel);  
            if (meth) {  
                log_and_fill_cache(cls, curClass, meth, sel);  
                methodPC = method_getImplementation(meth);  
                goto done;  
            }  
        }  
      
        // No implementation found. Try method resolver once.  
        // 6. 如果还是找不到就转发  
        if (!triedResolver) {  
            unlockForMethodLookup();  
            _class_resolveMethod(cls, sel);  
            // Don't cache the result; we don't hold the lock so it may have   
            // changed already. Re-do the search from scratch instead.  
            triedResolver = YES;  
            goto retry;  
        }  
      
        // No implementation found, and method resolver didn't help.   
        // Use forwarding.  
      
        _cache_addForwardEntry(cls, sel);  
        methodPC = &_objc_msgForward_internal;  
      
     done:  
        unlockForMethodLookup();  
      
        // paranoia: look for ignored selectors with non-ignored implementations  
        assert(!(ignoreSelector(sel)  &&  methodPC != (IMP)&_objc_ignored_method));  
      
        return methodPC;  
    }  
    

    不同的类可以有相同的方法名,方法链表中根据方法名去查找具体的方法实现的。
    IMP是一个函数指针,这个被指向的函数包含一个接收消息的对象id(自指针),调用方法的选标SEL(方法名)和不定个数的方法参数,并返回一个id。

    BOOL class_addMethod(Class cls,SEL名称,IMP imp,const char *类型)

    给CLS添加一个新的方法,若干CLS这个存在则方法报道查看失败

    下面来看代码

     class_addMethod(Class cls, SEL name, IMP imp, const charchar *types)  
    {  
        if (!cls) return NO;  
      
        rwlock_write(&runtimeLock);  
        IMP old = addMethod(newcls(cls), name, imp, types ?: "", NO);  
        rwlock_unlock_write(&runtimeLock);  
        return old ? NO : YES;  
    }  
      
    static IMP addMethod(class_t *cls, SEL name, IMP imp, const charchar *types, BOOL replace)  
    {  
        IMP result = NULL;  
      
        rwlock_assert_writing(&runtimeLock);  
      
        assert(types);  
        assert(isRealized(cls));  
      
        method_t *m;  
        // 1. 在自己的类的方法列表里找这个方法  
        if ((m = getMethodNoSuper_nolock(cls, name))) {  
            // already exists  
            if (!replace) {  
                // 不取代, 返回 m->imp  
                result = _method_getImplementation(m);  
            } else {  
                // 取代, 设置 cls 的 m 方法实现为 imp  
                result = _method_setImplementation(cls, m, imp);  
            }  
        } else {  
            // fixme optimize  
            // 2. 建立一个method_list_t节点  
            method_list_t *newlist;  
            newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1);  
            newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;  
            newlist->count = 1;  
            newlist->first.name = name;  
            newlist->first.types = strdup(types);  
            if (!ignoreSelector(name)) {  
                newlist->first.imp = imp;  
            } else {  
                newlist->first.imp = (IMP)&_objc_ignored_method;  
            }  
      
            // 3. 把newlist加到cls的方法列表里  
            BOOL vtablesAffected = NO;  
            attachMethodLists(cls, &newlist, 1, NO, &vtablesAffected);  
            // 4. 刷新cls缓存  
            flushCaches(cls);  
            if (vtablesAffected) flushVtables(cls);  
      
            result = NULL;  
        }  
      
        return result;  
    }  
    

    泪目, 这里就是直接设置replace == YES.

    void method_exchangeImplementations(Method m1_gen, Method m2_gen)

    交换2个方法的实现指针

    void method_exchangeImplementations(Method m1_gen, Method m2_gen)  
    {  
        method_t *m1 = newmethod(m1_gen);  
        method_t *m2 = newmethod(m2_gen);  
        if (!m1  ||  !m2) return;  
      
        rwlock_write(&runtimeLock);  
      
        if (ignoreSelector(m1->name)  ||  ignoreSelector(m2->name)) {  
            // Ignored methods stay ignored. Now they're both ignored.  
            m1->imp = (IMP)&_objc_ignored_method;  
            m2->imp = (IMP)&_objc_ignored_method;  
            rwlock_unlock_write(&runtimeLock);  
            return;  
        }  
          
        // 交换2个方法的实现指针  
        IMP m1_imp = m1->imp;  
        m1->imp = m2->imp;  
        m2->imp = m1_imp;  
      
        if (vtable_containsSelector(m1->name)  ||    
            vtable_containsSelector(m2->name))   
        {  
            // Don't know the class - will be slow if vtables are affected  
            // fixme build list of classes whose Methods are known externally?  
            flushVtables(NULL);  
        }  
      
        // fixme catch NSObject changing to custom RR  
        // cls->setCustomRR();  
      
        // fixme update monomorphism if necessary  
      
        rwlock_unlock_write(&runtimeLock);  
    }  
    

    其实这里有个坑, Method是怎么来的呢, 通过class_getInstanceMethod,如果子类没有的话,会返回父类的方法, 如果这个时候在用method_exchangeImplementations替换,会把父类替的方法替换掉,这显然不是我们想要的.所以呢,我们的method swizzle通常是这么写

    static void XY_swizzleInstanceMethod(Class c, SEL original, SEL replacement)  
    {  
        Method a = class_getInstanceMethod(c, original);  
        Method b = class_getInstanceMethod(c, replacement);  
      
        if (class_addMethod(c, original, method_getImplementation(b), method_getTypeEncoding(b)))  
        {  
            class_replaceMethod(c, replacement, method_getImplementation(a), method_getTypeEncoding(a));  
        }  
        else  
        {  
            method_exchangeImplementations(a, b);   
        }  
    }  
    

    IMP method_getImplementation(Method method)

    返回method的实现指针

    代码如下, 没什么好说的,其实就是返回method->imp

    IMP method_getImplementation(Method m)  
    {  
        return _method_getImplementation(newmethod(m));  
    }  
      
    static IMP _method_getImplementation(method_t *m)  
    {  
        if (!m) return NULL;  
        return m->imp;  
    }  
    

    IMP method_setImplementation(Method method, IMP imp)

    设置方法的新的实现指针, 返回旧的实现指针

    IMP method_setImplementation(Method m, IMP imp)  
    {  
        // Don't know the class - will be slow if vtables are affected  
        // fixme build list of classes whose Methods are known externally?  
        IMP result;  
        rwlock_write(&runtimeLock);  
        result = _method_setImplementation(Nil, newmethod(m), imp);  
        rwlock_unlock_write(&runtimeLock);  
        return result;  
    }  
      
    static IMP _method_setImplementation(class_t *cls, method_t *m, IMP imp)  
    {  
        rwlock_assert_writing(&runtimeLock);  
      
        if (!m) return NULL;  
        if (!imp) return NULL;  
      
        if (ignoreSelector(m->name)) {  
            // Ignored methods stay ignored  
            return m->imp;  
        }  
      
        // 替换方法的实现指针  
        IMP old = _method_getImplementation(m);  
        m->imp = imp;  
      
        // No cache flushing needed - cache contains Methods not IMPs.  
      
        if (vtable_containsSelector(newmethod(m)->name)) {  
            // Will be slow if cls is NULL (i.e. unknown)  
            // fixme build list of classes whose Methods are known externally?  
            flushVtables(cls);  
        }  
      
        // fixme catch NSObject changing to custom RR  
        // cls->setCustomRR();  
      
        // fixme update monomorphism if necessary  
      
        return old;  
    }  
    

    method_getTypeEncoding(Method m)

    返回方法m的参数和返回值的描述的字串

    这个就是直接返回m->types

    相关文章

      网友评论

          本文标题:objc_method(转)

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