问题
一个类的分类重写了这个类的方法后,那么这个类的这个方法将失效,那么原方法失效,分类方法生效的原因是什么?
+load
+load 方法是当类或分类被添加到 Objective-C runtime 时被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。子类的 +load 方法会在它的所有父类的 +load 方法之后执行,而分类的 +load 方法会在它的主类的 +load 方法之后执行。但是不同的类之间的 +load 方法的调用顺序是不确定的。
1 | 原因就在这里,因为加载顺序是父类先+load,然后子类+load,然后分类+load,那么如果分类重写子类方法:首先子类+load,将方法添加到类的方法列表methodLists,然后分类+load,将重写的方法添加到方法列表中 |
但是这里存在几点疑问:
- 方法列表methodLists里是否会有两个SEL相同的方法?
- 如果会有,这两个方法在方法列表中的顺序是怎样的?(顺序决定哪个被调用)
1 |
|
看输出:
1 | 2017-07-19 21:38:13.593 TestRuntimeProperty[27827:1306334] instanceMethod:newMethod |
可见:
- 方法列表里会存在两个SEL相同的方法。
- 实际调用时,调用的是分类添加的方法,即分类添加的方法在方法列表methodLists的这个数组的顶部
答案已经很明确了:
1 | 后+load的类的方法,后添加到方法列表,而这时的添加方式又是插入顶部添加,即[methodLists insertObject:category_method atIndex:0]; 所以objc_msgSend遍历方法列表查找SEL 对应的IMP时,会先找到分类重写的那个,调用执行。然后添加到缓存列表中,这样主类方法实现永远也不会调到。 |
多个category实现同一个方法
如果多个分类同时重写同一个方法,执行顺序又是怎样的呢?
对于多个分类同时重写同一个方法,Xcode在运行时是根据buildPhases->Compile Sources里面的顺序从上至下编译的,那么很明显就像子类和分类一样,后编译的后load,即后添加到方法列表,所以后编译的分类,方法会放到方法列表顶部,调用的时候先执行。
1 |
|
看输出:
1 | 2017-07-19 22:18:13.593 TestRuntimeProperty[28385:1331972] instanceMethod:newMethod |
1 | 总结:类的加载顺序,决定方法的添加顺序,调用的时候,后添加的方法会先被找到,所以调用的始终是后加载的类的方法实现。 |
+initialize
+initialize 方法是在类或它的子类收到第一条消息之前被调用的,这里所指的消息包括实例方法和类方法的调用。也就是说 +initialize 方法是以懒加载的方式被调用的,如果程序一直没有给某个类或它的子类发送消息,那么这个类的 +initialize 方法是永远不会被调用的。那这样设计有什么好处呢?好处是显而易见的,那就是节省系统资源,避免浪费。