【iOS】category重写方法的调用
来源:互联网 发布:长沙java招聘信息 编辑:程序博客网 时间:2024/05/22 13:02
前两天工程中,出现了一个类的两个分类(自己实现了一个,第三方SDK里有一个),同时实现了一个方法名相同的方法,当时就产生了一个疑问,当实际调用时调的是哪个分类的方法呢?
一、category重写主类方法
首先先来看看这一点,相信大部分人已经知道,如果一个类的分类重写了这个类的方法后,那么这个类的这个方法将失效,起作用的将会是分类的那个重写方法,在分类重写的时候Xcode也会给出相应警告:分类实现将会替代主类实现
那么原方法失效,分类方法生效的原理是?
想弄清这点先来看一下类的初始化,首先oc是动态语言,建立在runtime 的基础上,同样类的初始化也是动态的,根类NSObject 的+load
和+initilize
两个方法,用于类的初始化,我们这里要着重看的是+load
方法:
+load
方法是当类或分类被添加到 Objective-C runtime 时被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。子类的 +load
方法会在它的所有父类的 +load
方法执行之后执行,而分类的 +load
方法会在它的主类的 +load
方法执行之后执行。但是不同的类之间的 +load
方法的调用顺序是不确定的。
原因就在这里,因为加载顺序是父类先+load,然后子类+load,然后分类+load,那么如果分类重写子类方法:首先子类+load,将方法添加到类的方法列表methodLists,然后分类+load,将重写的方法添加到方法列表中,但是这里存在几点疑问:
1. 方法列表methodLists里是否会有两个SEL相同的方法?
2. 如果会有,这两个方法在方法列表中的顺序是怎样的?(顺序决定哪个被调用)
下面来求证一下:
#import "TestCategory.h"/*主类实现*/@implementation TestCategory- (void)newMethod { NSLog(@"主类");}@end#import "TestCategory+add.h"/*分类一实现*/@implementation TestCategory (add)- (void)newMethod { NSLog(@"分类一");}@end#import <objc/runtime.h>#import "TestCategory.h"@implementation ViewController- (void)viewDidLoad { [super viewDidLoad]; id LenderClass = objc_getClass("TestCategory"); unsigned int outCount, i; //获取实例方法列表 Method *methodList = class_copyMethodList(LenderClass, &outCount); for (i=0; i<outCount; i++) { Method method = methodList[i]; NSLog(@"instanceMethod:%@", NSStringFromSelector(method_getName(method))); } //调用一下 TestCategory *tCategory = [[TestCategory alloc]init]; [tCategory newMethod];}
看输出:
2017-07-19 21:38:13.593 TestRuntimeProperty[27827:1306334] instanceMethod:newMethod2017-07-19 21:38:13.593 TestRuntimeProperty[27827:1306334] instanceMethod:newMethod2017-07-19 21:38:13.594 TestRuntimeProperty[27827:1306334] 分类一
可见:
1. 方法列表里会存在两个SEL相同的方法。
2. 实际调用时,调用的是后添加的方法,即后添加的方法在方法列表methodLists的这个数组的顶部
答案已经很明确了:后+load的类的方法,后添加到方法列表,而这时的添加方式又是插入顶部添加,即[methodLists insertObject:category_method atIndex:0];
所以objc_msgSend遍历方法列表查找SEL 对应的IMP时,会先找到分类重写的那个,调用执行。然后添加到缓存列表中,这样主类方法实现永远也不会调到。
(注:methodLists和method的结构定义可以看下我上篇文章-【iOS】Runtime解读)
二、多个category实现同一个方法
但是如果多个分类同时重写同一个方法,执行顺序又是怎样的呢?
答案是:对于多个分类同时重写同一个方法,Xcode在运行时是根据buildPhases->Compile Sources里面的顺序从上至下编译的,那么很明显就像子类和分类一样,后编译的后load,即后添加到方法列表,所以后编译的分类,方法会放到方法列表顶部,调用的时候先执行。
添加代码验证一下:
#import "TestCategory+addAgain.h"/*分类二实现*/@implementation TestCategory (addAgain)- (void)newMethod { NSLog(@"分类二");}@end
看输出:
2017-07-19 22:18:13.593 TestRuntimeProperty[28385:1331972] instanceMethod:newMethod2017-07-19 22:18:13.593 TestRuntimeProperty[28385:1331972] instanceMethod:newMethod2017-07-19 21:18:13.593 TestRuntimeProperty[28385:1331972] instanceMethod:newMethod2017-07-19 22:18:13.594 TestRuntimeProperty[28385:1331972] 分类一
结果输出仍然是分类一
,那就说明"TestCategory+add.h"
在buildPhases->Compile Sources里面的顺序是靠下的,看下buildPhases的确如此:
总结一句话:类的加载顺序,决定方法的添加顺序,调用的时候,后添加的方法会先被找到,所以调用的始终是后加载的类的方法实现。
- 【iOS】category重写方法的调用
- IOS Category不要重写原有类方法
- category 方法调用的顺序
- ios分类category里的方法调用了但不执行
- iOS重写父类中的方法时要先调用父类中的方法的原因
- 040.category 调用私有方法
- 040.category 调用私有方法
- JAVA调用重写的祖父方法
- 调用被子类重写的方法
- ios的category
- ios的category
- iOS Category的正确理解
- 为什么不要在category中重写一个类原有的方法
- iOS开发之category动态添加属性的方法------关联
- iOS 重写pop跳转的方法
- 方法重写,抽象方法必须重写,抽象类。super.父类里面的方法调用方法。
- 虚拟方法的重写和静态方法的调用
- iOS方法重写
- SpringMVCAop编程(对比Struts2的拦截器)
- 通信数据分析平台(二)
- Android 5.0以上 app全局字体替换
- C# 方法异步执行
- MongoCollection::findOne没有办法完成排序的工作
- 【iOS】category重写方法的调用
- Android 即时通讯
- 散列的基础知识
- VMware Ubuntu配置虚拟机和主机互相ping通
- python基本数据类型
- Linux常用命令整理
- Objective-C属性关键字浅析(下)
- web.xml中配置的welcome-file 不生效
- Fragment的传值,使用,事务,Add和Replace