## 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_method结构体
objc_method
objc_method 是类的一个方法的描述
定义如下
- typedef struct objc_method *Method;
- struct objc_method {
- SEL method_name; // 方法名称
- charchar *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 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;
- }
我们用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 charchar *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