美文网首页
class_addMethod

class_addMethod

作者: s2mh | 来源:发表于2017-11-11 14:07 被阅读78次

    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

          本文链接:https://www.haomeiwen.com/subject/xfdwmxtx.html