底层原理总结 — Category3

2020-02-25  本文已影响0人  踩坑小分队
问题

Person类中有一个run方法,
PersonCategory1中也有一个run方法,
PersonCategory2中也有一个run方法,
调用[Person run]时响应的是哪个方法?

答案:

根据两个分类的编译顺序,调用的是后编译的Category中的run方法。

编译顺序.png
原因:

runtimePerson的元类对象中的类方法和Category中的类方法合并了,逆序<编译顺序>获取分类指针,并且将Category中的类方法,放在了方法列表的前面。所以调用run方法的时候,优先找到的是Category中的类方法。

方法列表顺序为:

【Category2】【Category1】【Person】


接着上面的问题继续:

Person中实现+(void)load方法,
PersonCategory1中实现+(void)load方法,
PersonCategory2中实现+(void)load方法
运行,调用谁的load方法?

答案:

都调用了

load -- Person
load -- Person (Extension1)
load -- Person (Extension2)

感觉和上面的问题对不上了。方法名一样的话不应该只调用一个吗?

原因分析:

下载objc源码看一下
源码地址:https://opensource.apple.com/tarballs/objc4/
顺着下面的方法直接找到最终目的地
void _objc_init(void)
load_images
call_load_methods();

void call_load_methods(void)
{
    static bool loading = NO;
    bool more_categories;

    loadMethodLock.assertLocked();

    // Re-entrant calls do nothing; the outermost call will finish the job.
    if (loading) return;
    loading = YES;

    void *pool = objc_autoreleasePoolPush();

    do {
        // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
        more_categories = call_category_loads();

        // 3. Run more +loads if there are classes OR more untried categories
    } while (loadable_classes_used > 0  ||  more_categories);

    objc_autoreleasePoolPop(pool);

    loading = NO;
}
第一步,调用所有的类的load方法
 // 1. Repeatedly call class +loads until there aren't any more
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

call_class_loads();中,
循环遍历所有的class
然后直接找到load方法,
直接进行调用<(*load_method)(cls, SEL_load);>

for (i = 0; i < used; i++) {
        Class cls = classes[i].cls;
        load_method_t load_method = (load_method_t)classes[i].method;
        if (!cls) continue; 

        if (PrintLoading) {
            _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
        }
        (*load_method)(cls, SEL_load);
    }
第二步,调用所有分类中的load方法

call_category_loads();

for (i = 0; i < used; i++) {
        Category cat = cats[i].cat;
        load_method_t load_method = (load_method_t)cats[i].method;
        Class cls;
        if (!cat) continue;

        cls = _category_getClass(cat);
        if (cls  &&  cls->isLoadable()) {
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s(%s) load]\n", 
                             cls->nameForLogging(), 
                             _category_getName(cat));
            }
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }

所以+ (void)load;方法class中和Category中的都会在runtime的时候调用

原因

调用顺序
先调用class中的load方法
再按照编译顺序调用Category中的load方法

上一篇下一篇

猜你喜欢

热点阅读