load和initial个人理解

328 阅读4分钟

load和initial个人理解

在上次面试字节的时候还记得面试官曾问过这个问题,当时虽然大体上答对了,但回想起来还是有些地方有些模糊,所以这次再对这个问题进行一下深入的分析。

load方法

+(void)load

load方法会在程序第一次加载到内存中时,即在main函数调用前就会调用,且仅会调用一次,与这个类或分类是否被用到无关。例如只要在系统文件中包含了某个类的文件,就算mian函数中没有引用该类的.h文件,那么当运行main函数之前,在objc运行时也会调用该类文件的load方法。

load方法调用的顺序基于:根类—>...—>父类—>子类—>分类。如果当中哪个类没有实现load方法也不会调用其父类load方法,该方法并不遵从继承规则。代码例如:

//父类
@interface BaseClass : NSObject
@end
@implementation BaseClass
+(void)load{
    NSLog(@"%@",self);
}
@end

//子类
@interface SubClass : BaseClass
@end
@implementation SubClass
+(void)load{
    NSLog(@"%@",self);
}
@end

//分类
@interface BaseClass (CategoryClass)
@end
@implementation BaseClass (CategoryClass)
+(void)load{
    NSLog(@"%@(CategoryClass)",self);
}
@end

main函数中执行,结果如下:

2020-05-19 15:22:27.360920+0800 effective-OC-test[97297:2862157] BaseClass
2020-05-19 15:22:27.361262+0800 effective-OC-test[97297:2862157] SubClass
2020-05-19 15:22:27.361323+0800 effective-OC-test[97297:2862157] BaseClass(CategoryClass)

load函数中调用self,返回的是类的类型:Class,与类方法中的+(Class)class和类对象方法的-(Class)class的返回结果一样

注意事项:因为在系统载入不同类的时候,载入顺序是不可预知的,所以在load方法中不能使用其他类,其他类在该类调用load方法前可能并未加载,所以使用其他类是不安全的。

initial方法

+(void)initialize

initial方法与load相似之处在于,在系统的加载过程中,只会加载一次,都不需要在类方法中去调用[super load][super initial]

不同点在于:

  • initial方法采用的是懒加载策略。只有当系统发消息给该类对象时就会加载该方法,例如在申请内存空间的类方法:+ (instancetype)alloc中就会调用该类的initial方法。

  • initial方法的调用顺序为:(分类)—>根类—>父类—>...—>字类,其中如果分类重写了initial方法,则会覆盖掉原来类的initial方法。如果字类没有覆写父类的initial方法,则会根据继承规则,当调用到字类时,会把父类的initial方法继承过来调用一遍。代码如下:

    //父类
    @interface BaseClass : NSObject
    @end
    @implementation BaseClass
    +(void)initialize{
        NSLog(@"initialize:%@",self);
    }
    @end
    
    //子类
    @interface SubClass : BaseClass
    @end
    @implementation SubClass
    +(void)initialize{
        NSLog(@"initialize:%@",self);
    }
    @end
    
    //父类的分类
    @interface BaseClass (CategoryClass)
    @end
    @implementation BaseClass (CategoryClass)
    +(void)initialize{
        NSLog(@"initialize:%@(catagoryClass)",self);
    }
    @end
    
    int main(int argc, const char * argv[]) {
        SubClass* sub=[SubClass new];
        return 0;
    }
    

    结果如下:

    2020-05-19 17:41:35.434074+0800 effective-OC-test[9418:3000392] initialize:BaseClass(catagoryClass)
    2020-05-19 17:41:36.151008+0800 effective-OC-test[9418:3000392] initialize:SubClass
    Program ended with exit code: 0
    

    由此可见,分类的initial方法已经将父类的方法覆写。

    如果这时候把子类:SubClass的initial方法注释掉,这时候再执行,结果如下:

    2020-05-19 17:44:53.126457+0800 effective-OC-test[9686:3003267] initialize:BaseClass(catagoryClass)
    2020-05-19 17:44:54.613843+0800 effective-OC-test[9686:3003267] initialize:SubClass(catagoryClass)
    Program ended with exit code: 0
    

    这时候会打印两遍分类的initial方法,只不过self的参数已经变为了SubClass。出现这种情况的原因就是因为SubClass没有覆写其父类(这时候已变为了父类的分类),当执行到字类时,系统会把父类(分类)的initial方法继承来调用,但这时候调用的self已经变为了当前的子类:SubClass,所以会打印出:initialize:SubClass(catagoryClass)

总结

  • 虽然可以控制不同类的initial方法调用顺序,但是同load方法一样,不建议在其内部调用其他自定义类,有时可能还会出现死锁现象。
  • load方法在执行的时候会阻塞整个应用,所以应减少其内部的执行操作,尽量不要在该方法内执行耗时任务。
  • initial方法内部通常用来对类内部的静态变量执行初始化操作。