iO Category加载流程1

98 阅读2分钟

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

关于Category 的使用,大家肯定都手到擒来了。

今天就来盘它Category

面试的时候可能被问到

类的load方法中,能调用分类的方法吗?category如何被加载的?+load 方法调用顺序?

基于objc4-779.1源码,debug来看一看Category。
首先第一个问题类的load方法中,能调用分类的方法吗?,答案是肯定的,可以

  • 通过runtime的入口函数可以知道先加载完分类,后调用的load方法
我们通过捋清楚Category的加载来一步步解答。
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();
    runtime_init();
    exception_init();
    cache_init();
    _imp_implementationWithBlock_init();

    _dyld_objc_notify_register(&map_images, load_images, unmap_image);
}

忽略掉一堆 init ,重点来看 _dyld_objc_notify_register(&map_images, load_images, unmap_image);
这个方法会注册3个事件并给出回调。
重点来看一下map_imagesload_images
从这俩个回调方法里看,你会发现Categorymap_images会加载完毕,而load_images会调用+load方法。这就解释了第一个问题:类的load方法中,能调用分类的方法

主要看点:map_images

map_images里来看看Category的加载。map_images最终会调用_read_images,这里就是核心部分了,从中间我们只看处理分类及类的部分

// Discover categories.
    for (EACH_HEADER) {
        bool hasClassProperties = hi->info()->hasCategoryClassProperties();

        auto processCatlist = [&](category_t * const *catlist) {
            for (i = 0; i < count; i++) {
                category_t *cat = catlist[i];
                //printf("%s\n",cat->name);
                Class cls = remapClass(cat->cls);
                locstamped_category_t lc{cat, hi};
                
                if (!cls) {
                    // Category's target class is missing (probably weak-linked).
                    // Ignore the category.
                    if (PrintConnecting) {
                        _objc_inform("CLASS: IGNORING category ???(%s) %p with "
                                     "missing weak-linked target class",
                                     cat->name, cat);
                    }
                    continue;
                }
                
                if (strcmp(cat->name, "HJJJJJ")==0) {
                    
                }
                // Process this category.
                if (cls->isStubClass()) {
                    // Stub classes are never realized. Stub classes
                    // don't know their metaclass until they're
                    // initialized, so we have to add categories with
                    // class methods or properties to the stub itself.
                    // methodizeClass() will find them and add them to
                    // the metaclass as appropriate.
                    if (cat->instanceMethods ||
                        cat->protocols ||
                        cat->instanceProperties ||
                        cat->classMethods ||
                        cat->protocols ||
                        (hasClassProperties && cat->_classProperties))
                    {
                        objc::unattachedCategories.addForClass(lc, cls);
                    }
                } else {
                    // First, register the category with its target class.
                    // Then, rebuild the class's method lists (etc) if
                    // the class is realized.
                    if (cat->instanceMethods ||  cat->protocols
                        ||  cat->instanceProperties)
                    {
                        if (cls->isRealized()) {
                            attachCategories(cls, &lc, 1, ATTACH_EXISTING);
                        } else {
                            objc::unattachedCategories.addForClass(lc, cls);
                        }
                    }
                    
                    if (cat->classMethods  ||  cat->protocols
                        ||  (hasClassProperties && cat->_classProperties))
                    {
                        if (cls->ISA()->isRealized()) {
                            attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
                        } else {
                            objc::unattachedCategories.addForClass(lc, cls->ISA());
                        }
                    }
                }
            }
        };
        processCatlist(_getObjc2CategoryList(hi, &count));
        processCatlist(_getObjc2CategoryList2(hi, &count));
    }
for (EACH_HEADER) {
        classref_t const *classlist = 
            _getObjc2NonlazyClassList(hi, &count);
        for (i = 0; i < count; i++) {
            Class cls = remapClass(classlist[i]);
            if (!cls) continue;
            //printf("11-%s\n",object_getClassName(cls));
            addClassTableEntry(cls);

            if (cls->isSwiftStable()) {
                if (cls->swiftMetadataInitializer()) {
                    _objc_fatal("Swift class %s with a metadata initializer "
                                "is not allowed to be non-lazy",
                                cls->nameForLogging());
                }
                // fixme also disallow relocatable classes
                // We can't disallow all Swift classes because of
                // classes like Swift.__EmptyArrayStorage
            }
            const char *tempChar;
            tempChar = object_getClassName(cls);
            if (strcmp(tempChar, "Person")==0) {
                
            }
            realizeClassWithoutSwift(cls, nil);
        }
    }

通过断点会发现 分类部分的代码 objc::unattachedCategories.addForClass(lc, cls);,只是将class和其对应的category做了一个映射,插入到map中。真正要看的还是realizeClassWithoutSwift 这个方法。