现在的位置: 首页 > 综合 > 正文

Objective-C method及相关方法分析

2018年05月25日 ⁄ 综合 ⁄ 共 7812字 ⁄ 字号 评论关闭

## 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替换,会把父类替的方法替换掉,这显然不是我们想要的.所以呢,我们的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

抱歉!评论已关闭.