zoukankan      html  css  js  c++  java
  • Objective-C method及相关方法分析

    ## Objective-C method及相关方法分析

    转载请注名出处 [http://blog.csdn.net/uxyheaven](http://blog.csdn.net/uxyheaven/article/details/38120335)

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

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

    objc_method


    objc_method 是类的一个方法的描写叙述


    定义例如以下

    typedef struct objc_method *Method;
    
    struct objc_method {
        SEL method_name; 		// 方法名称
        char *method_typesE;	// 參数和返回类型的描写叙述字串
        IMP method_imp;			// 方法的详细的实现的指针
    } 

    Method 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 name)


    返回cls的name方法的调用地址


    定义例如以下
    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(self指针), 调用方法的选标SEL(方法名), 及不定个数的方法參数, 并返回一个id。


    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)


    给cls加入一个新的方法,若干cls存在这种方法则返回失败


    以下来看代码

    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *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 char *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;
    }

    我们用class_addMethod时, replace == NO, 所以cls已经存在这种方法的时候加入是失败的


    IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)


    替换cls的name方法的指针

    IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
    {
        if (!cls) return NULL;
    
        return _class_addMethod(cls, name, imp, types, YES);
    }

    泪目, 这里就是直接设置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
  • 相关阅读:
    MVC通过后台注解来添加对数据的验证。
    HTML赋值方法练习
    HTML辅助方法的练习一
    第一次接触MVC Models概念
    部分视图的理解
    使用布局文件(Layout)
    springboot基本配置及快速启动
    springboot代码测试注意事项
    logback日志的基本使用
    springboot快速创建项目框架
  • 原文地址:https://www.cnblogs.com/brucemengbm/p/7016320.html
Copyright © 2011-2022 走看看