经过前面几篇论文关于Runtime部分已经介绍差不多了,这篇算是一个总结性的文章吧,其实+initialize 和 +load 已经在之前的几篇博客中都做了十分详细的介绍,这里放在一起无非就是对比一下,增加下记忆,所以这篇博客不会写得很长很复杂,好了言归正传:

+initialize
  • initialize的特点是惰性调用方式,它的调用时机是在对某个对象发送第一条消息的时候,也就是说如果我们不对某个对象发送消息,那么这个对象的initialize是不会被调用的。

  • 在分类中也可以实现initialize方法,只不过分类中的initialize方法会覆盖主类中的initialize方法,如果在多个分类中同时实现了initialize方法,那么只有最后被编译的那个分类的initialize方法会被调用。它会强制先向父类先发送 +initialize。

  • 与 load 不同,initialize 方法调用时,所有的类都已经加载到了内存中。

  • 其作用也非常局限,一般我们只会在 initialize 方法中进行一些常量的初始化。

+ (void)initialize {
if (self == [FLAnimatedImage class]) {
//......
}
}
+load

+load 方法相对于 +initialize 来说更为常见它是我们开发过程中可以接触到的调用时机最靠前的方法,和initialize不同的是它的调用不是惰性的,在主函数运行之前,load 方法就会被调用,并且它只会在程序调用期间调用一次,最重要的是如果在类与分类中都实现了 load 方法,它们都会被调用,不像+initialize 在分类中实现的方法会被覆盖,但是在使用load方法的时候需要注意,由于load方法的运行时间过早,所以可能不是一个理想的环境,因为它不能保证某些类可能需要在在其它类之前加载,但是在这个时间点,所有的 framework 都已经加载到了运行时中,所以调用 framework 中的方法都是安全的,同时需要注意的是重载Class 的 +load 方法时不能调父类super。

load / initialize 对比

方法 load initialize
是否惰性 否,都会被调用,并且只会被调用一次 是,只有在第一次向某个对象发送消息的时候调用
调用时机 main之前 main之后,第一次向某个对象发送消息的时候
在分类中的行为 在分类中和在主类中都会被调用,不会发生覆盖,并且先父类,再主类,再父类分类,主类分类 分类中的会覆盖主类中的initialize,所有分类中只有最后编译的那个生效,会强制先向父类先发送 +initialize
在调用的时候是否全部类已经加载完毕
attribute

关于__attribute__有很多很好的文档,大家如果看了还不大明白的话可以在下面文档中找答案,这里只想列出在开发过程中比较常用的几个:

  • attribute((objc_requires_super))

有时候我们自己编写的框架需要给别人用的时候,某个方法在使用的时候必须先调用父类的方法,一种做法是写注释,但是如果使用者不看注释就gg了,所以比较好的方式是使用__attribute__((objc_requires_super)) 如果我们没有调用父类的这个方法编译器就会发出警告。

  • attribute((unavailable(“not available”)))

告诉编译器方法不可用,如果使用了就会编译失败,提示错误。比如自定义了初始化方法,为了防止别人调用init初始化,就可以使用它来提示错误,这个在开发中也很常见,但是一般写成:

- (instancetype)init NS_UNAVAILABLE; 
  • attribute((deprecated(“该方法已经废弃”)))

用于在编译时提示方法过时:

- (void)deprecatedMethod:(NSString *)string __attribute__((deprecated("该方法已经废弃,请使用xxx方法")));
- (void)deprecatedMethod DEPRECATED_ATTRIBUTE;
  • attribute((cleanup))

声明到一个变量或者block上,当这个变量作用域结束时,调用指定的一个函数. 这个个人在时机工作中用得不多,大家可以看sunnyxx的黑魔法__attribute__((cleanup))这篇博客。

  • attribute((overloadable))

这个个人在时机工作中也用得不多,它主要用于C语言函数,可以定义若干个函数名相同,但参数不同的方法,调用时编译器会自动根据参数选择函数原型.

__attribute__((overloadable)) void print(NSString *string){
NSLog(@"%@",string);
}

__attribute__((overloadable)) void print(int num){
NSLog(@"%d",num);
}
  • attribute((objc_runtime_name(“NSObject”)))

这个用于将类或协议的名字在编译时指定成另一个,这个没用过。

__attribute__((objc_runtime_name("NSObject")))
@interface IDLObject :NSObject
  • attribute((objc_subclassing_restricted))

这个用于禁止被继承的时候使用,很少用到。

  • attribute((constructor)) / attribute((destructor))

最后介绍的是我们开发过程中经常用到的两个,在用之前需要和load区分开来,我们在将到load方法的时候有提到它的不足之处是它调用的时候某些类可能还没有被加载完成,所以不能在load方法中使用我们自己的类,这样很可能类都没被加载起来。那么__attribute__((constructor))的好处是什么呢?在__attribute__((constructor))标志的方法执行的时候,所有 Class 都已经加载完成,所以可以使用任意的类,并且它可以单独放在一个文件中,不用挂载在一个 Class 中,它和load的方法一样都是在main还未执行的时候调用。如果有多个 constructor 并且想按照指定顺序执行的话,可以写成 attribute((constructor(101)))这种形式,1 ~ 100 为系统保留,所以一般以101 为开始,里面的数字越小优先级越高。

[推荐文档]
Contents
  1. 1. +initialize
  2. 2. +load
  3. 3. attribute
  4. 4. [推荐文档]