Objective-C method及相關方法分析,objective-cmethod

來源:互聯網
上載者:User

Objective-C method及相關方法分析,objective-cmethod
## 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替換,會把父類替的方法替換掉,這顯然不是我們想要的.所以呢,我們的移魂大發通常是這麼寫

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

objective-c 的方法名?

description , 直譯, 就 描述的意思. 表示此方法返回此類的描述資訊.

description 這個方法是NSOjbect類的方法,
而我們在寫類的時候, 一般都會繼承NSObject.
也就是說, 所有的類, 都有description方法.

再說它的用途, 看API定義知道它返回NSString*,
最常用的地方就是, NSLog列印一個類對象的時候,
會自動調用這個對象的 description 方法.

其他的常用方法有 init, dealloc. 分別代表 初始化對象, 和回收時的自訂功能 .
 
objective-c 執行個體方法\類方法怎調用

在書寫了類的聲明和實現後,應用程式如何去調用它呢?
在Objective-c中,調用方法的簡單格式如下:
1[執行個體 方法]; 如: [person setAge:32]; 其中 person是Person類的執行個體。
或者是:
2 [類名 方法名]; 如:NSString str = [NSDate date]; 這是直接調用類NSdate中的方法date來得到當前日期和時間。
在Objective-c中,調用一個類或執行個體的方法,也稱為給這個類或執行個體發訊息(message)。類的執行個體稱為“接收方”。所以,通用方法調用的格式也可以理解為:
[接收方 訊息];
在術語上,整個運算式也叫做訊息運算式。這是官方的正式稱呼。
當然,一個方法可能會有參數,也有可能會有多個參數,因此完整的方法調用格式如下:
[接收方法 名子1:參數1 名子2:參數2 名子3:參數 ... ]
如:
[person setAge:32];
[person setName:@"sam" andSecondName:@"job"];
註:在多參數方法調用時,可以省略從第二個開始的方法名子。
如:
[person setName:@"sam" :@"job"];
一個方法中還可以調用另外一個方法調用,如:
[NSString stringWithFormat:[test format]];
註:方法的調用一定要加上中括弧“[........]”
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.