load
系统加载load方法大致可以梳理成如下几步:
- 准备load方法
- 递归获取superclass,获取load方法,加入待加载class load方法表(所以父类的load在最前)
- 按照category编译顺序加载category中的load方法,加入到另一个待加载category load方法表
- 从头到尾取出class load方法表,获取imp,直接执行(获取的是函数指针imp,所以不走msg_send)
- 再取category load方法表,执行。
所以,可以看出,load方法的执行顺序是 父类 -> 子类 -> category,同时是直接执行imp,不走msg_send,所以也不会执行查找函数方法表的流程,父类子类跟category之间是独立的
initialize
跟load不同,initialize的调用时机是第一次使用该类。
调用顺序上跟load类似,也是递归父类,但之后是直接调用msg_send(selector)。因为走得是msg_send,所以会查找方法表,所以category会覆盖类的initialize;同时,如果当前类没有实现initialize方法,也会因为查找方法表调用到父类的方法,所以父类initialize方法可能被调用多次。
总结 | +load | +initialize |
---|---|---|
调用时机 | 被添加到 runtime 时 | 收到第一条消息前,可能永远不调用 |
调用顺序 | 父类->子类->分类 | 父类->子类 |
调用次数 | 1次 | 多次 |
是否需要显式调用父类实现 | 否 | 否 |
是否沿用父类的实现 | 否 | 是 |
分类中的实现 | 类和分类都执行 | 覆盖类中的方法,只执行分类的实现 |
参考: