+load、+initialize和__attribute__((constructor))

事情缘起于全局变量初始化。众所周知,全局变量只有像整数、字符串等才可以直接在.m文件初始化,而NSArray、NSColor等不能直接初始化值。要让这些全局变量只初始化一次,有+load、+initialize和attribute((constructor))三种方式可选。

1. load

load方法是类在被运行时加载的时候调用

  • 父类在子类前调用;依赖的framework先调用。
  • 如果类没有实现此静态方法,则不调用
  • 本类和Category都实现了load,则都调用,且本类先调用

load最大的问题是不同的类调用顺序不确定。比如代码有A和B两个类。在A类load时候调用父类或framework里的类没问题,调用B类就不确定了。

2. initialize

initialize只有在类被第一次使用的时候调用,这种懒加载的方式很有诱惑力。然而,initialize最大的缺陷是它是基于OC消息机制。所以,如果子类没有实现initialize,那么会继续向父类发消息,直到找到为止。因此,如果类A实现initialize,A的子类Aa没实现。假如A和Aa都被用到,A的initialize方法就会被调用2次
通常这不是我们本意,常见的做法是在initialize方法里判断self是不是本类。

3. attribute((constructor))

则个是GCC的扩展语法(黑魔法),由它修饰过的函数,会在main函数之前调用。原理是在ELF的.ctors段增加一条函数引用,加载器在执行main函数前,检查.ctror section,并执行里面的函数。如果有多个attribute((constructor))修饰的函数有依赖,他们调用顺序是不确定的(应该也没人真的这样干吧)。

上面三种方法在app中的调用顺序是

load -> attribute((constructor)) -> main -> initialize

initialize的性能不错,不会影响程序启动。然而最大的缺陷是,要保证这些全局变量只被自己使用,等自己初始化后别人才能使用,而且,initialize容易写错导致多次调用。除非是那种比较耗资源的对象,一般不用。load在继承顺上有保证,而且还能在Category中使用,特定的场合很适用。比如这个iOSViewArchDemo1。综上,attribute((constructor))应该是最适合的一般初始化选择。

大家怎么看?

你可能感兴趣的:(+load、+initialize和__attribute__((constructor)))