iOS load 和 initialize
+(void)load调用时机
load方法的加载和这个类是否被使用到无关,这也是为什么可以在ios端能够做到无痕埋点的原因。子类是否重写父类的load方法和这个方法的调用无关,所以也就不需要在子类的方法里面调用父类方法,他的调用在init main方法之前,只会被调用一次且线程安全。
+(void)load{ }的调用顺序
调用父类+(void)load,然后调用创建的子类+(void)load(注意:多个子类按子类的创建先后依次调用),最后调用类别category +(void)load(注意:多个类别按类别的创建先后依次调用),然后进入app main函数,进入didFinishLaunchingWithOptions。
注意⚠️:类只要写了+(void)load,不管有没有使用都会调用。
新建三个类GrandPar->Parent->Son和两个类别(GrandPar+addtion,GrandPar+addtion1,Son+addtion,Son+addtion1)分别在里面实现了load方法,调用顺序如下:
GrandPar-load
Parent-load
Son-load
GrandPar-addtion-load
GrandPar-addtion1-load
Son-addtion-load
Son-addtion1-load
+(void)initialize调用时机
initialize的调用时机是发生在init main方法以后,他发生调用是在为该类的生成一个对象的时候,不管子类有没有实现initialize的方法,在使用该类时都会依次调用父类的initialize的方法。和load方法不同的是当类别和类都实现的该方法,那么类的initialize方法不会被调用会被类别覆盖。
+(void) initialize{ }的调用情况(因为是第一次调用类时,所以+(void) initialize{ }的调用时在代码层,就是开发者能看到的编码)
第一次调用一个类时,从祖先类依次往自己类调用+(void) initialize{ },(各祖先类中有类别category只调用类别的+(void) initialize),中间类没有写+(void) initialize的调用一次中间类的直系父类+(void) initialize。依次到调用自己类+(void) initialize(自己类有类别的只调用类别的+(void) initialize,多个类别只调用后创建的)
如果中间类中有非第一次调用的类,则从这个中间类的子类开始依次调用,如果本类没有写+(void) initialize,则多调用一次父类的+(void) initialize(此时如果父类是第一次调用则调用两次,如果非第一次调用也需要调用一次(保证每个类第一次调用都会走一次+(void) initialize))
load 和 initialize方法的区别:
1、 调用方式:
1> load是根据函数地址直接调用;
2> initialize是通过objc_msgSend调用。
2、调用时刻:
1> load是runtime加载类、分类的时候调用(只会调用一次);
2> initialize 是类第一次接受到消息的时候调用,每一个类只会 initialize一次(父类的 initialize会被调用多次。不冲突,子类没有,就调父类的。)
3、调用顺序:
load
1.先调用类的load;
先编译的类,优先调用load;
先调用父类的load,再调用子类的。
再调用分类的load。
先编译的分类,优先调用load。
initialize
1.先初始化父类;
2.再出初始化字类(可能最终调用的是父类的 initialize方法)。
注意⚠️:正常不需要手动调用+load 和+ initialize (如果手动调用+load 和+ initialize 跟正常的函数调用一样)
load和initialize 总结
一、+load类方法
当类被引用进项目的时候就会执行load函数(在main函数开始执行之前),与这个类是否被用到无关,每个类的load函数只会自动调用一次.由于load函数是系统自动加载的,因此不需要再调用[super load],否则父类的load函数会多次执行。
1.当父类和子类都实现load函数时,父类的load方法执行顺序要优先于子类2.当一个类未实现load方法时,不会调用父类load方法3.类中的load方法执行顺序要优先于类别(Category)4.当有多个类别(Category)都实现了load方法,这几个load方法都会执行,但执行顺序不确定(其执行顺序与类别在Compile Sources中出现的顺序一致)5.当然当有多个不同的类的时候,每个类load 执行顺序与其在Compile Sources出现的顺序一致
二、+initialize类方法
即使类文件被引用进项目,但是没有使用,initialize不会被调用假如这个类放到代码中,而这段代码并没有被执行,这个函数是不会被执行的。类或者其子类的第一个方法被调用前调用由于是系统自动调用,也不需要再调用 [super initialize] ,否则父类的initialize会被多次执行父类的initialize方法会比子类先执行子类未实现initialize方法时,会调用父类initialize方法,子类实现initialize方法时,会覆盖父类initialize方法.当有多个Category都实现了initialize方法,会覆盖类中的方法,只执行一个(会执行Compile Sources 列表中最后一个Category 的initialize方法)
三、两者的异同
1、相同点
load和initialize会被自动调用,不能手动调用它们。如果父类和子类都被调用,父类的调用一定在子类之前子类实现了load和initialize的话,会隐式调用父类的load和initialize方法。load和initialize方法内部使用了锁,因此它们是线程安全的。2、不同点
调用顺序不同,以main函数为分界,+load方法在main函数之前执行,+initialize在main函数之后执行。子类中没有实现+load方法的话,不会调用父类的+load方法;而子类如果没有实现+initialize方法的话,也会自动调用父类的+initialize方法。+load方法是在类被装在进来的时候就会调用,+initialize在第一次给某个类发送消息时调用(比如实例化一个对象),并且只会调用一次,是懒加载模式,如果这个类一直没有使用,就不会调用到+initialize方法。四、使用场景
1.+load一般是用来交换方法Method Swizzle,由于它是线程安全的,而且一定会调用且只会调用一次,通常在使用UrlRouter的时候注册类的时候也在+load方法中注册。
2.+initialize方法主要用来对一些不方便在编译期初始化的对象进行赋值,或者说对一些静态常量进行初始化操作。
load和initialize方法都会在实例化对象之前调用load执行在main函数以前,initialize执行main函数之后这两个方法会被自动调用,不能手动调用它们。load和initialize方法都不用显示的调用父类的方法而是自动调用子类没有initialize方法也会调用父类的方法,而load方法则不会调用父类initialize方法对一个类而言只会调用一次(Person、或者是Person+Category)都是一个Perosn类。load方法则是每个都会调用,只要你写了load方法,添加到工程都会实现。load方法通常用来进行Method Swizzle,initialize方法一般用于初始化全局变量或静态变量。load和initialize方法内部使用了锁,因此它们是线程安全的。实现时要尽可能保持简单,避免阻塞线程,不要再使用锁。
参考:https://baijiahao.baidu.com/s?id=1670083688956388773&wfr=spider&for=pc