category 方法调用的顺序

来源:互联网 发布:相同格式表格数据合并 编辑:程序博客网 时间:2024/06/07 04:08

一、category重写主类方法

在项目中我们经常会使用category(类别)来为已有的类添加新的方法。我们知道,如果我们重写原有类的方法,则类别的方法会替换掉原有类的方法,并且类别中会有警告的信息
截图

原方法失效,分类方法生效的原理是什么?

  1. 这里看一下类的初始化,首先oc是动态语言,建立在runtime 的基础上,同样类的初始化也是动态的,根类NSObject 的+load 和+initilize两个方法,用于类的初始化,我们这里要着重看的是+load方法:

  2. +load 方法是当类或分类被添加到 Objective-C runtime 时被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。子类的 +load 方法会在它的所有父类的 +load 方法执行之后执行,而分类的 +load 方法会在它的主类的 +load 方法执行之后执行

  3. 原因就在这里,因为加载顺序是父类先+load,然后子类+load,然后分类+load,那么如果分类重写子类方法:首先子类+load,将方法添加到类的方法列表methodLists,然后分类+load,将重写的方法添加到方法列表中,但是这里存在几点疑问:

A. 方法列表methodLists里是否会有两个SEL相同的方法?
B. 如果会有,这两个方法在方法列表中的顺序是怎样的?(顺序决定哪个被调用)

到这里很多人会猜:methodList 里面会有两个SEL 相同的方法名,但执行其中一个

#import "likeObj.h"@implementation likeObj-(void)test {    NSLog(@"原来的类");}@end#import "likeObj+likeCate.h"@implementation likeObj (likeCate)-(void)test {    NSLog(@"类别的类");}@end//使用Runtime 来查看方法#import "ViewController.h"#import <objc/runtime.h>#import "likeObj.h"#import "likeObj+likeCate.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {    [super viewDidLoad];    likeObj * li = [[likeObj alloc]init];    [li test];    id obj = objc_getClass("likeObj");    unsigned int count ,i;    Method *methodList = class_copyMethodList(obj, &count);    for ( i =0 ; i< count; i++) {        Method method = methodList[i];        NSLog(@"------%@",NSStringFromSelector(method_getName(method)));    }}@end// 打印为2017-11-16 20:06:16.671 categoryDemo[6420:2222139] 类别的类2017-11-16 20:06:18.781 categoryDemo[6420:2222139] ------test2017-11-16 20:06:19.417 categoryDemo[6420:2222139] ------test

由代码可以看出,猜想是对的
可见:
1. 方法列表里会存在两个SEL相同的方法。
2. 实际调用时,调用的是后添加的方法,即后添加的方法在方法列表methodLists的这个数组的顶部

后+load的类的方法,后添加到方法列表,而这时的添加方式又是插入顶部添加,即[methodLists insertObject:category_method atIndex:0]; 所以objc_msgSend遍历方法列表查找SEL 对应的IMP时,会先找到分类重写的那个,调用执行。然后添加到缓存列表中,这样主类方法实现永远也不会调到。

二、多个category实现同一个方法

多个分类同时重写同一个方法,执行顺序又是怎样的呢?

对于多个分类同时重写同一个方法,Xcode在运行时是根据buildPhases->Compile Sources里面的顺序从上至下编译的,就像子类和分类一样,后编译的后+ load,即后添加到方法列表,所以后编译的分类,方法会放到方法列表顶部,调用的时候先执行
所以会先调用下面的类的方法,此时如果你鼠标拖动两个类别的顺序,会随着你拖动的顺序改变的
这里写图片描述

总结一句话: 类的加载顺序,决定方法的添加顺序,调用的时候,后添加的方法会先被找到,所以调用的始终是后加载的类的方法实现。

原创粉丝点击