美文网首页SDWebImage
1、Category知识点整理

1、Category知识点整理

作者: Jack__Lee | 来源:发表于2020-09-04 11:08 被阅读0次

    1、什么是 Category

    Category 是 Objective-C 2.0 之后添加的语言特性,Category 的主要作用是为已经存在的类添加方法

    2、Category 的作用

    • 可以减少单个文件的体积
    • 可以把不同的功能组织到不同的Category中
    • 可以按需加载
    • 声明私有方法
    • 把framework的私有方法公开

    3、Category 的数据结构

    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods;
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
        // Fields below this point are not always present on disk.
        struct property_list_t *_classProperties;
    
        method_list_t *methodsForMeta(bool isMeta) {
            if (isMeta) return classMethods;
            else return instanceMethods;
        }
    
        property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
    };
    

    4、Category 是如何被加载的

    • dyld是苹果的动态加载器,用来加载 image(注意这里 image 不是指图片,而是 Mach-O 格式的二进制文件)。
    • 当程序启动时,系统内核首先会加载 dyld , 而 dyld 会将我们 APP 所依赖的各种库加载到内存空间中,其中就包括 libobjc 库(OC和runtime), 这些工作,是在 APP 的 main 函数执行前完成的。
    • _objc_init 是Object-C runtime 的入口函数,在这里面主要功能是读取 Mach-O 文件 OC 对应的 Segment seciton ,并根据其中的数据代码信息,完成为 OC 的内存布局,以及初始化 runtime 相关的数据结构。

    5、运行时源码解析

    // 1、程序启动的时候
    void _objc_init(void)
    {
        static bool initialized = false;
        if (initialized) return;
        initialized = true;
        
        // fixme defer initialization until an objc-using image is found?
        environ_init();
        tls_init();
        static_init();
        lock_init();
        exception_init();
        // 注册三个回调函数
        // map_images:dyld将image加载进内存 runtime读取二进制文件并保存 将分类加载到当前的类中
        // load_images:初始化image、load方法在这个时候执行
        _dyld_objc_notify_register(&map_images, load_images, unmap_image);
    }
    
    void map_images_nolock(unsigned mhCount, const char * const mhPaths[],
                      const struct mach_header * const mhdrs[])
    {
        static bool firstTime = YES;
        header_info *hList[mhCount];
        uint32_t hCount;
        size_t selrefCount = 0;
    
        if (hCount > 0) {
            // oc相关的section 初始化
            _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses);
        }
    }
    
    //      function name                 content type     section name
    GETSECT(_getObjc2SelectorRefs,        SEL,             "__objc_selrefs");
    GETSECT(_getObjc2MessageRefs,         message_ref_t,   "__objc_msgrefs");
    GETSECT(_getObjc2ClassRefs,           Class,           "__objc_classrefs");
    GETSECT(_getObjc2SuperRefs,           Class,           "__objc_superrefs");
    GETSECT(_getObjc2ClassList,           classref_t,      "__objc_classlist");
    GETSECT(_getObjc2NonlazyClassList,    classref_t,      "__objc_nlclslist");
    GETSECT(_getObjc2CategoryList,        category_t *,    "__objc_catlist");
    GETSECT(_getObjc2NonlazyCategoryList, category_t *,    "__objc_nlcatlist");
    GETSECT(_getObjc2ProtocolList,        protocol_t *,    "__objc_protolist");
    GETSECT(_getObjc2ProtocolRefs,        protocol_t *,    "__objc_protorefs");
    GETSECT(getLibobjcInitializers,       UnsignedInitializer, "__objc_init_func");
    
    /***********************************************************************
    * _read_images主要的工作就是将category的方法和属性通过remethodizeClass方法添加在class上,如果是类方法的话则添加到元类上。
    addUnattachedCategoryForClass的作用是将category添加到NXMap中,以方便之后取出来使用。
    **********************************************************************/
    void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
    {
        header_info *hi;
        uint32_t hIndex;
        size_t count;
        size_t i;
        Class *resolvedFutureClasses = nil;
        size_t resolvedFutureClassCount = 0;
        static bool doneOnce;
        TimeLogger ts(PrintImageTimes);
    
        runtimeLock.assertLocked();
        // Discover categories.
        for (EACH_HEADER) {
            category_t **catlist =
                _getObjc2CategoryList(hi, &count);
            bool hasClassProperties = hi->info()->hasCategoryClassProperties();
    
            for (i = 0; i < count; i++) {
                category_t *cat = catlist[i];
                Class cls = remapClass(cat->cls);
    
                if (!cls) {
                    // Category's target class is missing (probably weak-linked).
                    // Disavow any knowledge of this category.
                    catlist[i] = nil;
                    if (PrintConnecting) {
                        _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
                                     "missing weak-linked target class",
                                     cat->name, cat);
                    }
                    continue;
                }
    
                // Process this category.
                // First, register the category with its target class.
                // Then, rebuild the class's method lists (etc) if
                // the class is realized.
                bool classExists = NO;
                if (cat->instanceMethods ||  cat->protocols
                    ||  cat->instanceProperties)
                {
                    addUnattachedCategoryForClass(cat, cls, hi);
                    if (cls->isRealized()) {
                        remethodizeClass(cls);
                        classExists = YES;
                    }
                    if (PrintConnecting) {
                        _objc_inform("CLASS: found category -%s(%s) %s",
                                     cls->nameForLogging(), cat->name,
                                     classExists ? "on existing class" : "");
                    }
                }
    
                if (cat->classMethods  ||  cat->protocols
                    ||  (hasClassProperties && cat->_classProperties))
                {
                    // 将category添加到NXMap中,以方便之后取出来使用。
                    addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                    if (cls->ISA()->isRealized()) {
                        remethodizeClass(cls->ISA());
                    }
                    if (PrintConnecting) {
                        _objc_inform("CLASS: found category +%s(%s)",
                                     cls->nameForLogging(), cat->name);
                    }
                }
            }
        }
    }
    
    /***********************************************************************
    * addUnattachedCategoryForClass
    * Records an unattached category.
    * Locking: runtimeLock must be held by the caller.
    **********************************************************************/
    static void addUnattachedCategoryForClass(category_t *cat, Class cls,
                                              header_info *catHeader)
    {
        runtimeLock.assertLocked();
    
        // DO NOT use cat->cls! cls may be cat->cls->isa instead
        NXMapTable *cats = unattachedCategories();
        category_list *list;
    
        list = (category_list *)NXMapGet(cats, cls);
        if (!list) {
            list = (category_list *)
                calloc(sizeof(*list) + sizeof(list->list[0]), 1);
        } else {
            list = (category_list *)
                realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
        }
        list->list[list->count++] = (locstamped_category_t){cat, catHeader};
        //
        NXMapInsert(cats, cls, list);
    }
    
    static void remethodizeClass(Class cls)
    {
        attachCategories(cls, cats, true /*flush caches*/);
    }
    
    /// 可以看出,attachCategories就是将方法、属性和协议添加到类cls中。
    static void
    attachCategories(Class cls, category_list *cats, bool flush_caches)
    {
        // fixme rearrange to remove these intermediate allocations
        method_list_t **mlists = (method_list_t **)
            malloc(cats->count * sizeof(*mlists));
        property_list_t **proplists = (property_list_t **)
            malloc(cats->count * sizeof(*proplists));
        protocol_list_t **protolists = (protocol_list_t **)
            malloc(cats->count * sizeof(*protolists));
    
        auto rw = cls->data();
    
        prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
        rw->methods.attachLists(mlists, mcount);
        free(mlists);
        if (flush_caches  &&  mcount > 0) flushCaches(cls);
    
        rw->properties.attachLists(proplists, propcount);
        free(proplists);
    
        rw->protocols.attachLists(protolists, protocount);
        free(protolists);
    }
    
    /// 这里要注意一下,attachLists会将新添加的list添加到前面去,这样Category如果有同名方法的话就会优先被调用到,也是为什么会覆盖类的同名方法的原因。
    void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
    
        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count; // 旧的大小
            uint32_t newCount = oldCount + addedCount; //
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            /**
             拷贝的操作
             void    *memmove(void *__dst, const void *__src, size_t __len);
             */
            memmove(array()->lists + addedCount, array()->lists,
                    oldCount * sizeof(array()->lists[0]));
            memcpy(array()->lists, addedLists,
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        }
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists,
                   addedCount * sizeof(array()->lists[0]));
        }
    }
    
    

    相关文章

      网友评论

        本文标题:1、Category知识点整理

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