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]];
註:方法的調用一定要加上中括弧“[........]”