class_addMethod

runtime版本objc4-723

class_addMethod申明于 runtime.h:

OBJC_EXPORT BOOL
class_addMethod(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);

实现于objc-class-old.mm:

BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
{
    IMP old;
    if (!cls) return NO;

    old = _class_addMethod(cls, name, imp, types, NO);
    return !old;
}

这里调用了同个文件的static IMP _class_addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)函数。该函数的主要功能是:

  • 判断是否存在name对应的Method
    1. 若存在,则返回该Method的实现;
    2. 若不存在,则创建一个含有一个Methodold_method_list,将其地址插入methodLists所指数组,并返回nil

该版本的struct objc_class中的methodLists成员是old_method_list **类型,而非objc_method_list **。申明于objc-runtime-old.h。

具体实现如下:

static IMP _class_addMethod(Class cls, SEL name, IMP imp, 
                            const char *types, bool replace)
{
    old_method *m;
    IMP result = nil;

    if (!types) types = "";

    mutex_locker_t lock(methodListLock);

    if ((m = _findMethodInClass(cls, name))) {
        // already exists
        // fixme atomic
        result = method_getImplementation((Method)m);
        if (replace) {
            method_setImplementation((Method)m, imp);
        }
    } else {
        // fixme could be faster
        old_method_list *mlist = 
            (old_method_list *)calloc(sizeof(old_method_list), 1);
        mlist->obsolete = fixed_up_method_list;
        mlist->method_count = 1;
        mlist->method_list[0].method_name = name;
        mlist->method_list[0].method_types = strdup(types);
        mlist->method_list[0].method_imp = imp;
        
        _objc_insertMethods(cls, mlist, nil);
        if (!(cls->info & CLS_CONSTRUCTING)) {
            flush_caches(cls, NO);
        } else {
            // in-construction class has no subclasses
            flush_cache(cls);
        }
        result = nil;
    }

    return result;
}

其中void _objc_insertMethods(Class cls, old_method_list *mlist, old_category *cat)实现在 objc-runtime-old.mm中。该函数的主要功能是:

  1. clsmethodLists为空,则将mlist赋予methodLists,函数返回;
  2. 调用_objcTweakMethodListPointerForClass(cls),确保methodLists指向一个old_method_list数组;
  3. methodLists所指数组已满,则将methodLists指向新的一个长度+1的数组;
  4. methodLists所指数组的所有元素右移一个位;
  5. mlist赋予methodLists所指数组第一个元素。

具体实现如下:

void _objc_insertMethods(Class cls, old_method_list *mlist, old_category *cat)
{
    old_method_list ***list;
    old_method_list **ptr;
    ptrdiff_t endIndex;
    size_t oldSize;
    size_t newSize;

    if (!cls->methodLists) {
        // cls has no methods - simply use this method list
        cls->methodLists = (old_method_list **)mlist;
        cls->setInfo(CLS_NO_METHOD_ARRAY);
        return;
    }

    // Log any existing methods being replaced
    if (PrintReplacedMethods) {
        int i;
        for (i = 0; i < mlist->method_count; i++) {
            extern IMP findIMPInClass(Class cls, SEL sel);
            SEL sel = sel_registerName((char *)mlist->method_list[i].method_name);
            IMP newImp = mlist->method_list[i].method_imp;
            IMP oldImp;

            if ((oldImp = findIMPInClass(cls, sel))) {
                logReplacedMethod(cls->name, sel, ISMETA(cls), 
                                  cat ? cat->category_name : nil, 
                                  oldImp, newImp);
            }
        }
    }

    // Create method list array if necessary
    _objcTweakMethodListPointerForClass(cls);
    
    list = &cls->methodLists;

    // Locate unused entry for insertion point
    ptr = *list;
    while ((*ptr != 0) && (*ptr != END_OF_METHODS_LIST))
        ptr += 1;

    // If array is full, add to it
    if (*ptr == END_OF_METHODS_LIST)
    {
        // Calculate old and new dimensions
        endIndex = ptr - *list;
        oldSize  = (endIndex + 1) * sizeof(void *);
        newSize  = oldSize + sizeof(old_method_list *); // only increase by 1

        // Grow the method list array by one.
        *list = (old_method_list **)realloc(*list, newSize);

        // Zero out addition part of new array
        bzero (&((*list)[endIndex]), newSize - oldSize);

        // Place new end marker
        (*list)[(newSize/sizeof(void *)) - 1] = END_OF_METHODS_LIST;

        // Insertion point corresponds to old array end
        ptr = &((*list)[endIndex]);
    }

    // Right shift existing entries by one
    bcopy (*list, (*list) + 1, (uint8_t *)ptr - (uint8_t *)*list);

    // Insert at method list at beginning of array
    **list = mlist;
}

static void _objcTweakMethodListPointerForClass(Class cls)的主要功能是:

  1. cls->methodLists有值,且cls->info不含有CLS_NO_METHOD_ARRAY,则函数返回;
  2. 创建一个长度为4的数组;
  3. 将原来的cls->methodLists赋予数组第一个元素;
  4. END_OF_METHODS_LIST赋予数组最后一个元素;
  5. 将数组的地址赋予cls->methodLists
  6. 取消cls->infoCLS_NO_METHOD_ARRAY标记。

具体实现如下:

static void _objcTweakMethodListPointerForClass(Class cls)
{
    old_method_list *   originalList;
    const int                   initialEntries = 4;
    size_t                          mallocSize;
    old_method_list **  ptr;

    // Do nothing if methodLists is already an array.
    if (cls->methodLists  &&  !(cls->info & CLS_NO_METHOD_ARRAY)) return;

    // Remember existing list
    originalList = (old_method_list *) cls->methodLists;

    // Allocate and zero a method list array
    mallocSize   = sizeof(old_method_list *) * initialEntries;
    ptr      = (old_method_list **) calloc(1, mallocSize);

    // Insert the existing list into the array
    ptr[initialEntries - 1] = END_OF_METHODS_LIST;
    ptr[0] = originalList;

    // Replace existing list with array
    cls->methodLists = ptr;
    cls->clearInfo(CLS_NO_METHOD_ARRAY);
}

Class的methodLists可能是:

  1. 空指针,此时Class没有方法,(info & CLS_NO_METHOD_ARRAY) == true。
  2. objc_method结构体指针,此时Class有一个method_list。
  3. objc_method结构体指针数组,此时Class有多个method_list。

你可能感兴趣的:(class_addMethod)