Objective-C method及相关方法分析

19 篇文章 0 订阅

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

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

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

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

objc_method

objc_method 是类的一个方法的描述


定义如下

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. typedef struct objc_method *Method;  
  2.   
  3. struct objc_method {  
  4.     SEL method_name;        // 方法名称  
  5.     charchar *method_typesE;    // 参数和返回类型的描述字串  
  6.     IMP method_imp;         // 方法的具体的实现的指针  
  7. }   

Method class_getInstanceMethod(Class aClass, SEL aSelector)

返回aClass的名为aSelector的方法


定义如下

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Method class_getInstanceMethod(Class cls, SEL sel)  
  2. {  
  3.     if (!cls  ||  !sel) return NULL;  
  4.   
  5.     return look_up_method(cls, sel, YES/*cache*/YES/*resolver*/);  
  6. }  
  7.   
  8. static Method look_up_method(Class cls, SEL sel, BOOL withCache, BOOL withResolver)  
  9. {  
  10.     Method meth = NULL;  
  11.     // 1. 找缓存,有过有就返回  
  12.     if (withCache) {  
  13.         meth = _cache_getMethod(cls, sel, &_objc_msgForward_internal);  
  14.         if (meth == (Method)1) {  
  15.             // Cache contains forward:: . Stop searching.  
  16.             return NULL;  
  17.         }  
  18.     }  
  19.     // 2. 找自身  
  20.     if (!meth) meth = _class_getMethod(cls, sel);  
  21.   
  22.     // 3. 找转发  
  23.     if (!meth  &&  withResolver) meth = _class_resolveMethod(cls, sel);  
  24.   
  25.     return meth;  
  26. }  

IMP class_getMethodImplementation(Class cls, SEL name)

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


定义如下
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. IMP class_getMethodImplementation(Class cls, SEL sel)  
  2. {  
  3.     IMP imp;  
  4.   
  5.     if (!cls  ||  !sel) return NULL;  
  6.   
  7.     imp = lookUpMethod(cls, sel, YES/*initialize*/YES/*cache*/);  
  8.   
  9.     // Translate forwarding function to C-callable external version  
  10.     if (imp == (IMP)&_objc_msgForward_internal) {  
  11.         return (IMP)&_objc_msgForward;  
  12.     }  
  13.   
  14.     return imp;  
  15. }  
  16.   
  17. PRIVATE_EXTERN IMP lookUpMethod(Class cls, SEL sel,   
  18.                                 BOOL initialize, BOOL cache)  
  19. {  
  20.     Class curClass;  
  21.     IMP methodPC = NULL;  
  22.     Method meth;  
  23.     BOOL triedResolver = NO;  
  24.   
  25.     // Optimistic cache lookup  
  26.     // 1. 先找下缓存  
  27.     if (cache) {  
  28.         methodPC = _cache_getImp(cls, sel);  
  29.         if (methodPC) return methodPC;      
  30.     }  
  31.   
  32.     // realize, +initialize, and any special early exit  
  33.     // 2. 初始化下这个类,为接下来做准备  
  34.     methodPC = prepareForMethodLookup(cls, sel, initialize);  
  35.     if (methodPC) return methodPC;  
  36.   
  37.   
  38.     // The lock is held to make method-lookup + cache-fill atomic   
  39.     // with respect to method addition. Otherwise, a category could   
  40.     // be added but ignored indefinitely because the cache was re-filled   
  41.     // with the old value after the cache flush on behalf of the category.  
  42.  retry:  
  43.     lockForMethodLookup();  
  44.   
  45.     // Ignore GC selectors  
  46.     if (ignoreSelector(sel)) {  
  47.         methodPC = _cache_addIgnoredEntry(cls, sel);  
  48.         goto done;  
  49.     }  
  50.   
  51.     // Try this class's cache.  
  52.     // 3. 先试着找缓存  
  53.     methodPC = _cache_getImp(cls, sel);  
  54.     if (methodPC) goto done;  
  55.   
  56.     // Try this class's method lists.  
  57.     // 4. 找自己的method列表  
  58.     meth = _class_getMethodNoSuper_nolock(cls, sel);  
  59.     if (meth) {  
  60.         log_and_fill_cache(cls, cls, meth, sel);  
  61.         methodPC = method_getImplementation(meth);  
  62.         goto done;  
  63.     }  
  64.   
  65.     // Try superclass caches and method lists.  
  66.     // 5. 找父类的缓存和method列表  
  67.     curClass = cls;  
  68.     while ((curClass = _class_getSuperclass(curClass))) {  
  69.         // Superclass cache.  
  70.         meth = _cache_getMethod(curClass, sel, &_objc_msgForward_internal);  
  71.         if (meth) {  
  72.             if (meth != (Method)1) {  
  73.                 // Found the method in a superclass. Cache it in this class.  
  74.                 log_and_fill_cache(cls, curClass, meth, sel);  
  75.                 methodPC = method_getImplementation(meth);  
  76.                 goto done;  
  77.             }  
  78.             else {  
  79.                 // Found a forward:: entry in a superclass.  
  80.                 // Stop searching, but don't cache yet; call method   
  81.                 // resolver for this class first.  
  82.                 break;  
  83.             }  
  84.         }  
  85.   
  86.         // Superclass method list.  
  87.         meth = _class_getMethodNoSuper_nolock(curClass, sel);  
  88.         if (meth) {  
  89.             log_and_fill_cache(cls, curClass, meth, sel);  
  90.             methodPC = method_getImplementation(meth);  
  91.             goto done;  
  92.         }  
  93.     }  
  94.   
  95.     // No implementation found. Try method resolver once.  
  96.     // 6. 如果还是找不到就转发  
  97.     if (!triedResolver) {  
  98.         unlockForMethodLookup();  
  99.         _class_resolveMethod(cls, sel);  
  100.         // Don't cache the result; we don't hold the lock so it may have   
  101.         // changed already. Re-do the search from scratch instead.  
  102.         triedResolver = YES;  
  103.         goto retry;  
  104.     }  
  105.   
  106.     // No implementation found, and method resolver didn't help.   
  107.     // Use forwarding.  
  108.   
  109.     _cache_addForwardEntry(cls, sel);  
  110.     methodPC = &_objc_msgForward_internal;  
  111.   
  112.  done:  
  113.     unlockForMethodLookup();  
  114.   
  115.     // paranoia: look for ignored selectors with non-ignored implementations  
  116.     assert(!(ignoreSelector(sel)  &&  methodPC != (IMP)&_objc_ignored_method));  
  117.   
  118.     return methodPC;  
  119. }  

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


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

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


下面来看代码

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. BOOL class_addMethod(Class cls, SEL name, IMP imp, const charchar *types)  
  2. {  
  3.     if (!cls) return NO;  
  4.   
  5.     rwlock_write(&runtimeLock);  
  6.     IMP old = addMethod(newcls(cls), name, imp, types ?: ""NO);  
  7.     rwlock_unlock_write(&runtimeLock);  
  8.     return old ? NO : YES;  
  9. }  
  10.   
  11. static IMP addMethod(class_t *cls, SEL name, IMP imp, const charchar *types, BOOL replace)  
  12. {  
  13.     IMP result = NULL;  
  14.   
  15.     rwlock_assert_writing(&runtimeLock);  
  16.   
  17.     assert(types);  
  18.     assert(isRealized(cls));  
  19.   
  20.     method_t *m;  
  21.     // 1. 在自己的类的方法列表里找这个方法  
  22.     if ((m = getMethodNoSuper_nolock(cls, name))) {  
  23.         // already exists  
  24.         if (!replace) {  
  25.             // 不取代, 返回 m->imp  
  26.             result = _method_getImplementation(m);  
  27.         } else {  
  28.             // 取代, 设置 cls 的 m 方法实现为 imp  
  29.             result = _method_setImplementation(cls, m, imp);  
  30.         }  
  31.     } else {  
  32.         // fixme optimize  
  33.         // 2. 建立一个method_list_t节点  
  34.         method_list_t *newlist;  
  35.         newlist = (method_list_t *)_calloc_internal(sizeof(*newlist), 1);  
  36.         newlist->entsize_NEVER_USE = (uint32_t)sizeof(method_t) | fixed_up_method_list;  
  37.         newlist->count = 1;  
  38.         newlist->first.name = name;  
  39.         newlist->first.types = strdup(types);  
  40.         if (!ignoreSelector(name)) {  
  41.             newlist->first.imp = imp;  
  42.         } else {  
  43.             newlist->first.imp = (IMP)&_objc_ignored_method;  
  44.         }  
  45.   
  46.         // 3. 把newlist加到cls的方法列表里  
  47.         BOOL vtablesAffected = NO;  
  48.         attachMethodLists(cls, &newlist, 1NO, &vtablesAffected);  
  49.         // 4. 刷新cls缓存  
  50.         flushCaches(cls);  
  51.         if (vtablesAffected) flushVtables(cls);  
  52.   
  53.         result = NULL;  
  54.     }  
  55.   
  56.     return result;  
  57. }  

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


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

替换cls的name方法的指针

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. IMP class_replaceMethod(Class cls, SEL name, IMP imp, const charchar *types)  
  2. {  
  3.     if (!cls) return NULL;  
  4.   
  5.     return _class_addMethod(cls, name, imp, types, YES);  
  6. }  

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


void method_exchangeImplementations(Method m1_gen, Method m2_gen)

交换2个方法的实现指针
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. void method_exchangeImplementations(Method m1_gen, Method m2_gen)  
  2. {  
  3.     method_t *m1 = newmethod(m1_gen);  
  4.     method_t *m2 = newmethod(m2_gen);  
  5.     if (!m1  ||  !m2return;  
  6.   
  7.     rwlock_write(&runtimeLock);  
  8.   
  9.     if (ignoreSelector(m1->name)  ||  ignoreSelector(m2->name)) {  
  10.         // Ignored methods stay ignored. Now they're both ignored.  
  11.         m1->imp = (IMP)&_objc_ignored_method;  
  12.         m2->imp = (IMP)&_objc_ignored_method;  
  13.         rwlock_unlock_write(&runtimeLock);  
  14.         return;  
  15.     }  
  16.       
  17.     // 交换2个方法的实现指针  
  18.     IMP m1_imp = m1->imp;  
  19.     m1->imp = m2->imp;  
  20.     m2->imp = m1_imp;  
  21.   
  22.     if (vtable_containsSelector(m1->name)  ||    
  23.         vtable_containsSelector(m2->name))   
  24.     {  
  25.         // Don't know the class - will be slow if vtables are affected  
  26.         // fixme build list of classes whose Methods are known externally?  
  27.         flushVtables(NULL);  
  28.     }  
  29.   
  30.     // fixme catch NSObject changing to custom RR  
  31.     // cls->setCustomRR();  
  32.   
  33.     // fixme update monomorphism if necessary  
  34.   
  35.     rwlock_unlock_write(&runtimeLock);  
  36. }  

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

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static void XY_swizzleInstanceMethod(Class c, SEL original, SEL replacement)  
  2. {  
  3.     Method a = class_getInstanceMethod(c, original);  
  4.     Method b = class_getInstanceMethod(c, replacement);  
  5.   
  6.     if (class_addMethod(c, original, method_getImplementation(b), method_getTypeEncoding(b)))  
  7.     {  
  8.         class_replaceMethod(c, replacement, method_getImplementation(a), method_getTypeEncoding(a));  
  9.     }  
  10.     else  
  11.     {  
  12.         method_exchangeImplementations(a, b);   
  13.     }  
  14. }  

IMP method_getImplementation(Method method)

返回method的实现指针


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

[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. IMP method_getImplementation(Method m)  
  2. {  
  3.     return _method_getImplementation(newmethod(m));  
  4. }  
  5.   
  6. static IMP _method_getImplementation(method_t *m)  
  7. {  
  8.     if (!m) return NULL;  
  9.     return m->imp;  
  10. }  

IMP method_setImplementation(Method method, IMP imp)

设置方法的新的实现指针, 返回旧的实现指针
[objc]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. IMP method_setImplementation(Method m, IMP imp)  
  2. {  
  3.     // Don't know the class - will be slow if vtables are affected  
  4.     // fixme build list of classes whose Methods are known externally?  
  5.     IMP result;  
  6.     rwlock_write(&runtimeLock);  
  7.     result = _method_setImplementation(Nil, newmethod(m), imp);  
  8.     rwlock_unlock_write(&runtimeLock);  
  9.     return result;  
  10. }  
  11.   
  12. static IMP _method_setImplementation(class_t *cls, method_t *m, IMP imp)  
  13. {  
  14.     rwlock_assert_writing(&runtimeLock);  
  15.   
  16.     if (!m) return NULL;  
  17.     if (!imp) return NULL;  
  18.   
  19.     if (ignoreSelector(m->name)) {  
  20.         // Ignored methods stay ignored  
  21.         return m->imp;  
  22.     }  
  23.   
  24.     // 替换方法的实现指针  
  25.     IMP old = _method_getImplementation(m);  
  26.     m->imp = imp;  
  27.   
  28.     // No cache flushing needed - cache contains Methods not IMPs.  
  29.   
  30.     if (vtable_containsSelector(newmethod(m)->name)) {  
  31.         // Will be slow if cls is NULL (i.e. unknown)  
  32.         // fixme build list of classes whose Methods are known externally?  
  33.         flushVtables(cls);  
  34.     }  
  35.   
  36.     // fixme catch NSObject changing to custom RR  
  37.     // cls->setCustomRR();  
  38.   
  39.     // fixme update monomorphism if necessary  
  40.   
  41.     return old;  
  42. }  

method_getTypeEncoding(Method m)

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


这个就是直接返回m->types
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值