objc_method(转)

文章地址: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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容