[iOS开发]runtime交换某个对象的两个方法

OC是一门动态语言,方法的调用本质上是利用objc_msgSend进行"发消息",也就是某类或某对象调用其某方法,本质上是向某个对象的指针发送了一条消息,在此之前方法和对象(或类)都没有真正确定下来即动态绑定,消息与方法的真正实现是在执行阶段绑定的,而非编译阶段.

所谓动态绑定,我举一个简单的C语言例子

#import <stdio.h> 

void Chinese() {  
    printf("Chinese book");  
}  
void Math() {  
    printf("Math book");  
}  

void doTheThing(int type) {  
    if (type == 0) {  
        Chinese();  
    } else {  
        Math();  
    }  
    return 0;  
}

对于上面这一类型到底是调用hello函数还是goodbye函数,这两个函数都是已经确定的,就像是有一本语文书一本数学书放在你面前,而你已经知道这两本中的一本最终会被你拿到手上,语文书和数学书已经是放在那里的了,不是动态改变啊的.

#import <stdio.h> 

void Chinese() {  
    printf("Chinese book");  
}  
void Math() {  
    printf("Math book");  
}  

void doTheThing(int type) {  
    void (*func)();  
    if (type == 0) {  
        func = Chinese;  
    } else {  
        func = Math;  
    }  
    func();  
    return 0;  
}

而对于这一种类型,我们可以看到我们声明了一个函数指针func,就好比你拿到了够买其中一本书的钱,只是钱在你手里,到底是买语文书还是数学书还没有确定,手上的钱是会动态改变的,这叫做动态绑定.

当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatch table)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatch table)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。 通过这种方式,message与方法的真正实现在执行阶段才绑定。

为了保证消息发送与执行的效率,系统会将全部selector和使用过的方法的内存地址缓存起来。每个类都有一个独立的缓存,缓存包含有当前类自己的 selector以及继承自父类的selector。查找调度表(dispatch table)前,消息发送系统首先检查receiver对象的缓存。

我们现在看一下objc_msgSend如何使用

先在控制器中定义一个方法

- (void)changeColor:(UIColor *)colorOne colorTwo:(UIColor *)colorTwo colorThree:(UIColor *)colorThree colorFour:(UIColor *)colorFour{
    static NSUInteger count = 0;
    NSUInteger k = count %4 ;
    switch (k) {
        case 0:
            self.view.backgroundColor = colorOne;
            break;
        case 1:
            self.view.backgroundColor = colorTwo;
            break;
        case 2:
            self.view.backgroundColor = colorThree;
            break;
        case 3:
            self.view.backgroundColor = colorFour;
            break;
        default:
            break;
    }
    count ++;
}

然后对其进行调用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    SEL change = @selector(changeColor:colorTwo:colorThree:colorFour:);
    UIColor *colorOne = [UIColor blueColor];
    UIColor *colorTwo = [UIColor greenColor];
    UIColor *colorThree = [UIColor redColor];
    UIColor *colorFour = [UIColor yellowColor];
//  这里到底有几个参数你就放几个id,当然你也可以直接指定类型
      ((void(*)(id,SEL, id,id,id,id))objc_msgSend)(self,change , colorOne, colorTwo,colorThree,colorFour);
}

交换两个方法

在实际开发中我们会遇到这样一个问题,当项目开发得差不多的时候,或者说到了项目迭代的时候,我们发现了内存泄露(如block的不规范使用导致),不知道到底是哪个View或者说是哪个控制器没有正常被回收.那么我们常用的做法就是在- (void)dealloc方法中打印某些字样,去控制台看到底是在哪些界面跳转或者回跳的时候哪些对象没有调用dealloc方法.

在这个时候我们要是去一个个文件中重写dealloc方法就太繁琐了,而且容易漏掉一些类.面对这种情景,使用分类,在分类中交换方法是最好的解决办法,而且它的实现不需要引入分类头文件

#import "UIView+dealloc.h"
#import <objc/runtime.h>
@implementation UIView (dealloc)

+ (void)load{
    Method m2 = class_getInstanceMethod([self class], @selector(myDealloc));
    Method m1 = class_getInstanceMethod([self class], NSSelectorFromString(@"dealloc"));
    method_exchangeImplementations(m2, m1);
}
//系统调用dealloc方法的时候会调用该方法
- (void)myDealloc{
    NSLog(@"%@挂了", self);
//此刻实际是在调用dealloc方法
    [self myDealloc];   
}

我这里写了一个UIView的分类,也就是说所有UIView的子类被回收的时候都能够调用这个犯法,其中+ (void)load 方法会被调用一次,它并不需要该文件被使用才会被调用,也就是在能内存中加载的时候就会被调用,且仅有一次,在这一次调用中我们把UIView的两个对象方法进行了替换(也就是在最早的时候就交换了方法,相当于把这两条神经给交换接上了).

以上就是一个经常遇到的的runtime替换两个方法的使用场景,若有写的不好的地方欢迎指出.

版权声明:本文版权归本文作者所有,始发于简书,如需转载请联系作者,违者必究.

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

推荐阅读更多精彩内容