美文网首页Objective-C
iOS:load与initialize

iOS:load与initialize

作者: 码小菜 | 来源:发表于2019-12-14 18:36 被阅读0次

目录
一,load方法
二,initialize方法
三,本质区别
四,基本使用
五,注意点

一,load方法

1,特点

  • 在类或分类被加载进内存时调用,且只调用一次

  • 优先调用类的,再调用分类的

  • 先编译的类优先调用,先编译的分类也优先调用

  • 调用子类的之前,会先调用父类的

  • 即使子类的没有实现也不会调用父类的

2,代码验证

  • 验证前四点
编译顺序
// Person
@implementation Person
+ (void)load {
    NSLog(@"Person load");
}
@end

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

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

// Animal
@implementation Animal
+ (void)load {
    NSLog(@"Animal load");
}
@end

// Dog(继承自Animal)
@implementation Dog
+ (void)load {
    NSLog(@"Dog load");
}
@end

@implementation Dog (Add1)
+ (void)load {
    NSLog(@"Dog (Add1) load");
}
@end

@implementation Dog (Add2)
+ (void)load {
    NSLog(@"Dog (Add2) load");
}
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"viewDidLoad");
}

// 打印
Animal load
Dog load
Person load
Person (Add1) load
Dog (Add1) load
Person (Add2) load
Dog (Add2) load
viewDidLoad
  • 验证第五点
// Animal
@implementation Animal
+ (void)load {
    NSLog(@"Animal load---%@", self);
}
@end

// Dog(继承自Animal)
@implementation Dog
@end

// Pig(继承自Animal)
@implementation Pig
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"viewDidLoad");
}

// 打印
Animal load---Animal
viewDidLoad

3,源码分析(源码下载地址

  • 加载load方法
void
load_images(const char *path __unused, const struct mach_header *mh)
{
    // Return without taking locks if there are no +load methods here.
    if (!hasLoadMethods((const headerType *)mh)) return;

    recursive_mutex_locker_t lock(loadMethodLock);

    // Discover load methods
    {
        mutex_locker_t lock2(runtimeLock);
        // 准备load方法
        prepare_load_methods((const headerType *)mh);
    }

    // Call +load methods (without runtimeLock - re-entrant)
    
    // 调用load方法
    call_load_methods();
}
  • 准备load方法
void prepare_load_methods(const headerType *mhdr)
{
    size_t count, i;

    runtimeLock.assertLocked();

    // 获取所有的类
    classref_t *classlist = 
        _getObjc2NonlazyClassList(mhdr, &count);
    // 遍历类列表
    for (i = 0; i < count; i++) {
        // 安排类的load方法
        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
        if (cls->isSwiftStable()) {
            _objc_fatal("Swift class extensions and categories on Swift "
                        "classes are not allowed to have +load methods");
        }
        realizeClassWithoutSwift(cls);
        assert(cls->ISA()->isRealized());
        // 将分类加入可执行load方法的分类列表中
        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
    
    // 先安排父类的load方法(递归调用)
    schedule_class_load(cls->superclass);
    // 将类加入可执行load方法的类列表中
    add_class_to_loadable_list(cls);
    cls->setInfo(RW_LOADED); 
}
  • 调用load方法
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) {
            // 调用类的load方法
            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;
}

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.
    
    // 遍历可执行load方法的类列表
    for (i = 0; i < used; i++) {
        // 取出类
        Class cls = classes[i].cls;
        // 取出类中的load方法
        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方法
        (*load_method)(cls, SEL_load);
    }
    
    // Destroy the detached list.
    if (classes) free(classes);
}

static bool call_category_loads(void)
{
    int i, shift;
    bool new_categories_added = NO;
    
    // Detach current loadable list.
    struct loadable_category *cats = loadable_categories;
    int used = loadable_categories_used;
    int allocated = loadable_categories_allocated;
    loadable_categories = nil;
    loadable_categories_allocated = 0;
    loadable_categories_used = 0;

    // Call all +loads for the detached list.
    
    // 遍历可执行load方法的分类列表
    for (i = 0; i < used; i++) {
        // 取出分类
        Category cat = cats[i].cat;
        // 取出分类中的load方法
        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方法
            (*load_method)(cls, SEL_load);
            cats[i].cat = nil;
        }
    }

    return new_categories_added;
}
二,initialize方法

1,特点

  • 在类第一次接收到消息时调用,且只调用一次

  • 类和分类只调用一个,优先调用分类的

  • 多个分类优先调用后编译的

  • 调用子类的之前,会先调用父类的

  • 如果子类的没有实现就会调用父类的,所以父类的可能会调用多次

2,代码验证

  • 验证前四点
编译顺序
// Animal
@implementation Animal
+ (void)initialize {
    NSLog(@"Animal initialize");
}
@end

// Dog(继承自Animal)
@implementation Dog
+ (void)initialize {
    NSLog(@"Dog initialize");
}
@end

@implementation Dog (Add1)
+ (void)initialize {
    NSLog(@"Dog (Add1) initialize");
}
@end

@implementation Dog (Add2)
+ (void)initialize {
    NSLog(@"Dog (Add2) initialize");
}
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Dog *dog1 = [Dog new];
    Dog *dog2 = [Dog new];
}

// 打印
Animal initialize
Dog (Add1) initialize
  • 验证第五点
// Animal
@implementation Animal
+ (void)initialize {
    NSLog(@"Animal initialize---%@", self);
}
@end

// Dog(继承自Animal)
@implementation Dog
@end

// Pig(继承自Animal)
@implementation Pig
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Dog *dog = [Dog new];
    Pig *pig = [Pig new];
}

// 打印
Animal initialize---Animal
Animal initialize---Dog
Animal initialize---Pig

3,源码分析

  • 寻找方法的实现(在类接收到消息时会调用此方法)
IMP lookUpImpOrForward(Class cls, SEL sel, id inst, 
                       bool initialize, bool cache, bool resolver)
{
    IMP imp = nil;
    bool triedResolver = NO;

    runtimeLock.assertUnlocked();

    // Optimistic cache lookup
    if (cache) {
        imp = cache_getImp(cls, sel);
        if (imp) return imp;
    }

    runtimeLock.lock();
    checkIsKnownClass(cls);

    if (!cls->isRealized()) {
        cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }

    // 如果该类需要初始化并且没有初始化
    if (initialize && !cls->isInitialized()) {
        // 在此方法中调用initializeNonMetaClass方法进行初始化
        cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
        // runtimeLock may have been dropped but is now locked again
    }

    return imp;
}
  • 初始化某个类
void initializeNonMetaClass(Class cls)
{
    assert(!cls->isMetaClass());

    Class supercls;
    bool reallyInitialize = NO;

    // Make sure super is done initializing BEFORE beginning to initialize cls.
    // See note about deadlock above.
    
    supercls = cls->superclass;
    // 如果父类没有初始化就先初始化父类(递归调用)
    if (supercls  &&  !supercls->isInitialized()) {
        initializeNonMetaClass(supercls);
    }
    
    if (reallyInitialize) {
#if __OBJC2__
        @try
#endif
        {
            // 调用initialize方法
            callInitialize(cls);

            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: finished +[%s initialize]",
                             pthread_self(), cls->nameForLogging());
            }
        }
#if __OBJC2__
        @catch (...) {
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: thread %p: +[%s initialize] "
                             "threw an exception",
                             pthread_self(), cls->nameForLogging());
            }
            @throw;
        }
        @finally
#endif
        {
            // Done initializing.
            lockAndFinishInitializing(cls, supercls);
        }
        return;
    }
}
  • 调用initialize方法
void callInitialize(Class cls)
{
    // 向需要初始化的类发送消息
    ((void(*)(Class, SEL))objc_msgSend)(cls, SEL_initialize);
    asm("");
}
三,本质区别

它们特点的不同源自于它们本质的不同

1,objc_msgSend函数(消息发送)

所有的方法调用都会转化为消息发送,消息发送首先会通过isasuperclass指针找到方法列表,然后在方法列表中顺序查找方法并进行调用

2,load方法

根据方法地址直接调用,不通过objc_msgSend函数

3,initialize方法

通过objc_msgSend函数进行调用

四,基本使用

1,系统会自动调用它们,不需要手动调用

2,系统会自动调用它们的父类方法,不需要手动调用

@implementation Person
+ (void)load {
//    [super load];
    NSLog(@"Person load");
}
+ (void)initialize {
//    [super initialize];
    NSLog(@"Person initialize");
}
@end

3,load方法一般用于method swizzling(方法交换),initialize方法一般用于初始化全局变量或静态变量

4,父类的initialize方法可能会调用多次,可以通过判断self来分开执行操作

@implementation Animal
+ (void)initialize {
    if (self == [Dog class]) {
        NSLog(@"Dog initialize");
    } else if (self == [Pig class]) {
        NSLog(@"Pig initialize");
    } else {
        NSLog(@"Animal initialize");
    }
}
@end
五,注意点

1,load方法在main函数前调用,initialize方法在main函数后调用

// main
int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSLog(@"main");
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

// Person
@implementation Person
+ (void)load {
    NSLog(@"Person load");
}
+ (void)initialize {
    NSLog(@"Person initialize");
}
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [Person new];
}

// 打印
Person load
main
Person initialize

2,这两个方法会阻塞线程,尽量避免在其中执行太多的操作

// Person
@implementation Person
+ (void)load {
    NSLog(@"Person load");
    sleep(3.0);
}
+ (void)initialize {
    NSLog(@"Person initialize");
    sleep(3.0);
}
@end

// 使用
- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [Person new];
    NSLog(@"viewDidLoad");
}

// 打印
17:15:14 Person load
17:15:17 Person initialize
17:15:20 viewDidLoad

3,这两个方法是线程安全的,系统在调用它们时加了锁

相关文章

网友评论

    本文标题:iOS:load与initialize

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