runtime入门

1.什么是运行时?

1.1 OC Runtime 是被忽略的特性之一
Objective-C 的 Runtime 是一个运行时库(Runtime Library),它是一个主要使用 C 和汇编写的库,为 C 添加了面相对象的能力并创造了 Objective-C。这就是说它在类信息(Class information) 中被加载,完成所有的方法分发,方法转发,等等。Objective-C runtime 创建了所有需要的结构体,让 Objective-C 的面相对象编程变为可能。

1.2 - RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制

  • 对于C语言,函数的调用在编译的时候会决定调用哪个函数。
  • 对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
  • 事实证明:
    • 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
    • 在编译阶段,C语言调用未实现的函数就会报错。

编译器会把[target doMethodWith:var1]; 转换为 objc_msgSend(target,@selector(doMethodWith:),var1);

1.3 运行时的属于 Method

  • 1.3.1 Instance Method(实例方法):以 ‘-’ 开始,比如 -(void)doFoo; 在对象实例上操作。
  • 1.3.2 Class Method(类方法):以 ‘+’ 开始,比如 +(id)alloc。

在c语言中,如果你编译成功,那么以实际的运行过程中,系统会严格的按照刚才编译的顺序执行;oc中的只要编译成功就好了,真正运行的时候,执行什么方法,再说吧,看看到时候是啥,充满了不确定性

2.小demo- 交换对象方法和类方法

2.1 写出两个对象方法,runjump,调用那个,就是打印出所在的方法,
2.2 我们要做的目标是,交换runjump的方法.即当调用run的时候,实际调用了jump的方法

//狗对象的.h文件
- (void)run;

- (void)jump;

+ (void)eat;

+(void)drink;

//狗对象的.m文件
- (void)run{
    NSLog(@"%s",__func__);
}

- (void)jump{
    NSLog(@"%s",__func__);
}

+ (void)eat{
    NSLog(@"%s",__func__);
}

+(void)drink{
    NSLog(@"%s",__func__);
}

viewController中调用狗对象,看看打印

    //获取对象的方法
    RTDog *dog = [[RTDog alloc] init];
    [dog run];
    [dog jump];
//运行结果如下
2016-09-10 09:04:06.902 RunTime[646:11432] -[RTDog run]
2016-09-10 09:04:06.902 RunTime[646:11432] -[RTDog jump]

现在去交换对象方法

    //交换方法
//获取对象方法用这个 ,第二个参数是是这个样子的~~~
    class_getInstanceMethod(__unsafe_unretained Class cls, SEL name)

    Method method1 = class_getInstanceMethod([RTDog class], @selector(run));
    Method method2 = class_getInstanceMethod([RTDog class], @selector(jump));
    method_exchangeImplementations(method1, method2);    
    //获取对象的方法
    RTDog *dog = [[RTDog alloc] init];
    [dog run];
    [dog jump];

运行结果如下

//方法被交换了!
2016-09-10 09:04:06.902 RunTime[646:11432] -[RTDog jump]
2016-09-10 09:04:06.902 RunTime[646:11432] -[RTDog run]

交换类方法

        Method method1 = class_getClassMethod([RTDog class], @selector(eat));
        Method method2 = class_getClassMethod([RTDog class], @selector(drink));
    method_exchangeImplementations(method1, method2);
    [RTDog eat];
    [RTDog drink];

结果如下

2016-09-10 09:21:45.387 RunTime[712:19847] +[RTDog drink]
2016-09-10 09:21:45.388 RunTime[712:19847] +[RTDog eat]
二.给控制器新写一个rt_dealloc方法.替换系统中的dealloc方法

所有的控制器销毁的时候,都会调用新写方法,这个可以统一管理销毁的事件

要执行好几个push操作

替换了控制器的dealloc方法只有,在替换的方法中我们打印数据

2016-09-10 15:58:39.067 RunTime[746:14220] -[UIViewController(RTExtension) rt_dealloc]-<UIViewController: 0x7fef72d2b840>
2016-09-10 15:59:27.303 RunTime[746:14220] -[UIViewController(RTExtension) rt_dealloc]-<UIViewController: 0x7fef72fb7100>
2016-09-10 15:59:27.821 RunTime[746:14220] -[UIViewController(RTExtension) rt_dealloc]-<UIViewController: 0x7fef72c38ca0>
2016-09-10 15:59:28.343 RunTime[746:14220] -[UIViewController(RTExtension) rt_dealloc]-<UIViewController: 0x7fef72c38730>

代码如下

+ (void)load{
    
    //交换方法
    
        Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));
        Method method2 = class_getInstanceMethod(self, @selector(rt_dealloc));
        method_exchangeImplementations(method1, method2);
}

- (void)rt_dealloc{
    NSLog(@"%s-%@",__func__,self);
}

1.先写一个控制器的分类,然后在load方法中去替换两个方法,load方法的调用时在一加载到内存中就调用,所以特别适合监听各种方法的替换过程。
2.既然已加载内存中药去替换,所以这里的.h文件是没有用的,直接删除就好 。但是会报错,#import "UIViewController+RTExtension.h"显示没有发现,直接替换成#import "UIKit/UiKit.h"就好了,这个是告诉系统我们有UIViewController这个控件的

三.在rt_dealloc执行之后,再去执行 dealloc方法

1.在rt_dealloc方法中,我们执行完了自己的东西,如何再去调用dealloc的数据?

举个栗子:将控制器 -3继承自ViewController,然后重写该控制器的dealloc方法

- (void)dealloc{
    NSLog(@"viewControoller被销毁了");
}

运行结果如下

RunTime[826:24134] viewControoller被销毁了
RunTime[826:24134] -[UIViewController(RTExtension) rt_dealloc]-<ViewController: 0x7fbb09757a00>

刚才不是说好了,交换了吗,怎么两个都调用了?
因为刚才的方法中这样写的 Method method1 = class_getInstanceMethod(self, NSSelectorFromString(@"dealloc"));我们写成class_getInstanceMethod(self, @selector(dealloc));这样是报错的。也就是ACR状态下,调用这个方法是要特殊处理的

所以,按照我这样写替换rt_deallocdealloc是有问题的,应当找个特定的处理方法,现在没想出来。不过,想刚刚说的run,jump,eat,drink方法都是没有错的。

假设刚刚的dealloc方法是可以的,(假设不需要特殊处理),在实际的开发中,我们替换了两个方法,但是我想执行完rt_dealloc再去执行dealloc方法,咋办?

千万不要这样写,是死循环

应该这样写

- (void)rt_dealloc{
    NSLog(@"%s-%@",__func__,self);
    [self rt_dealloc];
}

因为此刻的rt_delloc已经替换成了dealloc

通过rt_imageNamed:替换imageNamed:方法

目的,通过前边的方法,替换后边的,如果是nil,打印信息

控制器1中放置一个imageView,然后拉线到外边,然后调用imageNamed:方法,加载图片
//控制器的方法,直接给照片赋值
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.imageV.image = [UIImage imageNamed:@"1323"];
}
@end
#import "UIKit/UiKit.h"
#import <objc/runtime.h>
@implementation UIImage (THExtension)
+ (void)load{
    //交换两个方法
    Method method1 = class_getClassMethod(self, @selector(imageNamed:));
    Method method2 = class_getClassMethod(self, @selector(rt_imageNamed:));
    method_exchangeImplementations(method1, method2);
}

+  (id)rt_imageNamed:(NSString *)name
{
    UIImage *image = [self rt_imageNamed:name];
    if (image == nil) {
        NSLog(@"图片是空的");
    }
    return image;
}
@end

1.删除了.h文件,因为直接在load方法中替换方法
2.+ (id)rt_imageNamed:(NSString *)name调用,实际上控制器调用的 iamgeNamed:方法
3.UIImage *image = [self rt_imageNamed:name];这个是调用的iamgeNamed:方法
4.调用的时候,还是执行过去的,不会影响任何的东西


这样做有什么好处?因为系统的方法不够完全,不能完全符合我的需求,所以我要按照自己的意愿做一些处理,然后通过运行时来改变他。类似的,我们经常遇到向数组,字典中添加nil的时候,我们通过addObject:方法添加对象,可以过滤掉nil并且给他个提示,也是写一个分类,然后判断,很简单

reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>

@implementation NSMutableArray (Extension)
+ (void)load{
    //交换两个方法 class_getInstanceMethod
    Method method1 = class_getInstanceMethod(self, @selector(addObject:));
    Method method2 = class_getInstanceMethod(NSClassFromNSString(@"__NSArrayM"), @selector(rt_addObject:));
    method_exchangeImplementations(method1, method2);
    
}

- (void)rt_addObject:(id)anObject{
    if (anObject) {
        [self rt_addObject:anObject];
    }else{
        NSLog(@"对象是nil,不能给你添加到数组中");
    }
}
@end
//控制器中的数组添加方法
- (void)viewDidLoad {
    [super viewDidLoad];
    NSMutableArray *arr = [NSMutableArray array];
    [arr addObject:@"4"];
    NSString *str = nil;
    [arr addObject:str];
}

NSArrayNSDictionary对应类不可以写成self,一定要写成对应的类簇形式,Google就行

参考链接

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

推荐阅读更多精彩内容

  • 初级 iOS 程序猿在实际项目开发中,很少有机会需要主动用到 runtime 相关的东西。 之前面试了不少同学,当...
    yehot阅读 4,209评论 4 8
  • 一、runtime简介 RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消...
    演员新之助阅读 331评论 0 0
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,697评论 7 64
  • 青春是什么呢!青春是围墙上的苍苔,撩拨人的心意,润湿你的心底。 我还有青春吗?青春是什么呢?我摸摸自己眼角的细纹,...
    亘木阅读 465评论 0 0