美文网首页iOS底层原理
小码哥底层原理笔记:Catgory的load方法和initial

小码哥底层原理笔记:Catgory的load方法和initial

作者: chilim | 来源:发表于2020-05-12 18:12 被阅读0次

我们先创建一个Person类和它的两个分类

@interface Person : NSObject

+ (void)test;

@end
@implementation Person

+ (void)load{
    NSLog(@"Person + load");
}

+ (void)test{
    NSLog(@"Person + test");
}

@end

@interface Person (Test1)

+ (void)test;

@end
@implementation Person (Test1)

+ (void)load{
    NSLog(@"Person(Test1) + load");
}

+ (void)test{
    NSLog(@"Person(Test1) + test");
}

@end
@interface Person (Test2)

+ (void)test;

@end
@implementation Person (Test2)

+ (void)load{
    NSLog(@"Person(Test2) + load");
}

+ (void)test{
    NSLog(@"Person(Test2) + test");
}

@end

我们调用[Person test];方法,打印如下:

2020-05-12 15:58:29.899321+0800 XMGTestProject[11526:7291123] Person + load
2020-05-12 15:58:29.900233+0800 XMGTestProject[11526:7291123] Person(Test1) + load
2020-05-12 15:58:29.900414+0800 XMGTestProject[11526:7291123] Person(Test2) + load
2020-05-12 15:58:29.900545+0800 XMGTestProject[11526:7291123] Person(Test2) + test

这里只打印了Test2分类的方法,因为编译时顺序Test2分类比Test1分类后编译。但是注意,每个类的+load方法都执行了一次这是为什么?我们查看runtime源码:

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
//首先会调用类的+load方法
        while (loadable_classes_used > 0) {
            call_class_loads();
        }

        // 2. Call category +loads ONCE
//然后再调用分类的+load方法
        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方法都将调用一次。
我们点进去call_class_loads()方法

static void call_class_loads(void)
{
    int i;
    
    // Detach current loadable list.
//数组里面存放所有类
    struct loadable_class *classes = loadable_classes;
    int used = loadable_classes_used;
    loadable_classes = nil;
    loadable_classes_allocated = 0;
    loadable_classes_used = 0;
    
    // Call all +loads for the detached list.
    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);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

我们看到+load方法的调用跟普通的方法调用不一样,它是直接从类里面拿到这个方法的地址,然后直接调用。所以每个类的+load方法都会调用。

+load方法的调用顺序

由上面源码可知,很显然+load方法的调用顺序是先调用原有类的+load方法,然后再调用分类的+load方法。那么原有类的+load方法调用顺序呢?如果有多个分类其调用顺序又是怎样呢?
先看原有类的+load方法调用顺序

void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertWriting();

    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    for (i = 0; i < count; i++) {
//将类放入数组中
        schedule_class_load(remapClass(classlist[i]));
    }

    category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
    for (i = 0; i < count; i++) {
        category_t *cat = categorylist[i];
        Class cls = remapClass(cat->cls);
        if (!cls) continue;  // category for ignored weak-linked class
        realizeClass(cls);
        assert(cls->ISA()->isRealized());
        add_category_to_loadable_list(cat);
    }
}

static void schedule_class_load(Class cls)
{
    if (!cls) return;
    assert(cls->isRealized());  // _read_images should realize

    if (cls->data()->flags & RW_LOADED) return;

    // Ensure superclass-first ordering
//递归,将父类传进去,把父类加进数组中
    schedule_class_load(cls->superclass);

    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}

从上面schedule_class_load()方法中可以看到会优先调用父类的+load方法。如果没有继承关系,那么就会按照编译的顺序去调用,先编译,先调用。
+load方法调用顺序总结:

1、首先是调用类的+load方法,按类的编译顺序调用,优先编译的先调用,调用子类的+load方法之前优先调用父类的+load方法
2、然后调用分类的+load方法,按分类的编译顺序调用,优先编译的先调用

面试题

1、Category中有load方法吗?load方法是什么时候调用?load方法能继承吗?
答:有load方法,load方法在runtime加载类、分类的时候调用,是可以继承的,但是一般情况下是不会主动调用load方法

相关文章

网友评论

    本文标题:小码哥底层原理笔记:Catgory的load方法和initial

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