iOS - Runtime class_replaceMethod源码

987 阅读1分钟

Runtime源码下载

替换一个方法的实现

1. API说明

/** 
* Replaces the implementation of a method for a given class.
* 
* @param cls The class you want to modify.
* @param name A selector that identifies the method whose implementation you want to replace.
* @param imp The new implementation for the method identified by name for the class identified by cls.
* @param types An array of characters that describe the types of the arguments to the method. 
*  Since the function must take at least two arguments—self and _cmd, the second and third characters
*  must be “@:” (the first character is the return type).
* 
* @return The previous implementation of the method identified by \e name for the class identified by \e cls.
* 
* @note This function behaves in two different ways:
*  - If the method identified by \e name does not yet exist, it is added as if \c class_addMethod were called. 
*    The type encoding specified by \e types is used as given.
*  - If the method identified by \e name does exist, its \c IMP is replaced as if \c method_setImplementation were called.
*    The type encoding specified by \e types is ignored.
*/
OBJC_EXPORT IMP _Nullable
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                   const char * _Nullable types) 
   OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
  1. 如果方法不存在,会先调用class_addMethod添加方法
  2. 如果方法存在,会将SEL的方法实现替换成传入的IMP

2. 源码

IMP 
class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
{
    if (!cls) return nil;

    mutex_locker_t lock(runtimeLock);
    return addMethod(cls, name, imp, types ?: "", YES);
}
/**********************************************************************
* addMethod
* fixme
* Locking: runtimeLock must be held by the caller
**********************************************************************/
static IMP 
addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
{
    IMP result = nil;

    runtimeLock.assertLocked();

    checkIsKnownClass(cls);
    
    ASSERT(types);
    ASSERT(cls->isRealized());

    method_t *m;
    if ((m = getMethodNoSuper_nolock(cls, name))) {
        // already exists
        if (!replace) {
            result = m->imp(false);
        } else {
            result = _method_setImplementation(cls, m, imp);
        }
    } else {
        // fixme optimize
        method_list_t *newlist;
        newlist = (method_list_t *)calloc(method_list_t::byteSize(method_t::bigSize, 1), 1);
        newlist->entsizeAndFlags = 
            (uint32_t)sizeof(struct method_t::big) | fixed_up_method_list;
        newlist->count = 1;
        auto &first = newlist->begin()->big();
        first.name = name;
        first.types = strdupIfMutable(types);
        first.imp = imp;

        addMethods_finish(cls, newlist);
        result = nil;
    }

    return result;
}


该方法最终调用与class_addMethod调用方法一致,唯一的区别是

        // already exists
        if (!replace) {
            result = m->imp(false);
        } else {
            result = _method_setImplementation(cls, m, imp);
        }