【iOS】category 重写方法的调用

前两天工程中,出现了一个类的两个分类(自己实现了一个,第三方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:newMethod
2017-07-19 21:38:13.593 TestRuntimeProperty[27827:1306334] instanceMethod:newMethod
2017-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:newMethod
2017-07-19 22:18:13.593 TestRuntimeProperty[28385:1331972] instanceMethod:newMethod
2017-07-19 21:18:13.593 TestRuntimeProperty[28385:1331972] instanceMethod:newMethod
2017-07-19 22:18:13.594 TestRuntimeProperty[28385:1331972] 分类一

结果输出仍然是分类一,那就说明"TestCategory+add.h"在buildPhases->Compile Sources里面的顺序是靠下的,看下buildPhases的确如此:

Compile Sources .m文件排序
Compile Sources .m文件排序

总结一句话:类的加载顺序,决定方法的添加顺序,调用的时候,后添加的方法会先被找到,所以调用的始终是后加载的类的方法实现。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,911评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,014评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,129评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,283评论 1 264
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,159评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,161评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,565评论 3 382
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,251评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,531评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,619评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,383评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,255评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,624评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,916评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,199评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,553评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,756评论 2 335

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,670评论 0 9
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 一、Runtime简介 Runtime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消...
    林安530阅读 1,056评论 0 2
  • 现在正值中国农历的三伏天,在中医看来现在是治疗很多慢性疾病的绝佳时期,也就是所谓的冬病夏治。 冬病夏治的渊源 冬病...
    叫我徐公子阅读 451评论 2 2
  • 听风声在沙沙作响 敲打着谁的愁肠 思念在一瞬间生长 才忘了夜多漫长
    Lady_Wohoiine阅读 378评论 0 0