Runtime大体介绍(附代码)

Runtime

一、简要介绍

  • C语言编译的时候如果有的方法没有实现是会报错的,但是oc不会,因为oc在编译的时候只管找得到这个方法就好,不管你这个方法实现了没有,所以这才导致在运行的时候,去找这个方法发现没有实现,所以就报错了,这也就是OC是动态运行的语言

  • 那么runtime是什么呢?runtime就像一个操作系统一样,oc就运行在这个操作系统上或者说runtime是一套纯c语言写的机制,然后平时我们用的oc方法都是这个runtime封装的,也就是说runtime就是oc的底层实现的机制

// 这句代码是oc的,那么oc是怎么去实现这个代码的呢
Person *p = [Person allow];

// 这句就是上面那句oc代码在底层的实现
// 也就是说,你写了上面的那句代码后,其实oc的实现,就是调用了
// objc_msgSend方法,这个方法的作用是谁发送什么方法
// 整句的意思就是Person类发送allow的消息
Person *p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc"));

// 现在知道runtime是个什么东西了把,其实就是一套纯C语言写的API,给OC调用的,实现了OC的底层实现机制


  • runtime的方法都是有前缀的,谁的事情谁开头

objc_msgSend // 发送方法, 是对象的事情,所以是objc开头



  • 在oc中方法调用的底层实现 :任何方法的调用都是发送一个消息,用runtime发送消息
// 这句就是Person类调用allow方法
// 底层的实现,就是Person发送了一个allow消息
Person *p = objc_msgSend(objc_getClass("Person"),sel_registerName("alloc"));


  • 使用runtime是需要导包的
#import <objc/message.h>

  • 使用runtime没有参数提醒的问题

- objc_msgsend()代码提醒没有参数,但是我们需要参数的

去项目,Build Setting msg 改为NO

二、应用

  • 利用runtime的消息机制,帮我们调用私有方法

  • 交换方法

  • 动态添加方法

  • 动态添加属性

  • runtime字典转模型(MJExtension的实现方式)

三、应用详细解释

1.为了装逼,仅仅装逼,oc的代码都可以换成runtime的API

  // oc创建
//    NSObject *objc = [[NSObject alloc] init];
//    
//    NSLog(@"%@",objc);
    
    // runtime创建
    
    // id:谁发送消息
    // SEL:发送什么消息
    // 以下代码就相当[NSObject alloc]
    id mySelf = objc_getClass("NSObject"); // 获取NSObject类
    SEL msgAlloc = sel_registerName("alloc"); // 发送alloc消息
    NSObject *objc = objc_msgSend(mySelf, msgAlloc); // NSObject类发送了alloc的消息
    
    objc = objc_msgSend(objc, sel_registerName("init")); // objc发送init消息
    
    NSLog(@"%@",objc);


2.利用runtime的消息机制,帮我们调用私有方法

  • 我们为什么要调用私有方法

开发觉得小伙伴有个类的私有方法写的很好,我想拿来用,所以这时候就只能通过runtime去拿

// 假如,我有一个类SendMsg,中有一个私有方法,正常的话oc是
调用不到私有方法的,所以我们用runtime来实现

 // 2.利用runtime的消息机制,帮我们调用私有方法
    
    SendMsg *sendMsg = [[SendMsg alloc] init];
    
    objc_msgSend(sendMsg, @selector(privateMethod));// 成功调用了私有方法
    
    // 回想下上面讲过的,oc方法的调用,就是发送一个消息
    // 那么上面那个代码就是发送一个privateMethod的消息



  • oc方法调用的流程怎么样?

1.调用实例方法:是去类对象的方法列表中找这个实例方法名

2.调用类方法: 是去元类中的方法列表找

3.每个类对象都会有一个isa指针指向自身

  • 调用流程

1.通过isa指针去对应的类中查找

2.注册方法编号

  • 好处

  • 根据方法编号快速找到对应的方法

3.根据方法编号去查找对应方法

4.找到只是最终函数实现的地址,根据地址去方法区调用实现的函数

image

3.交换方法-对系统的方法感到不满

1.要让[UIImage imageNamed:@"1.png"];添加成功输出成功

  • 最蠢方法
  // 图片
   UIImage *image = [UIImage imageNamed:@"1.png"];
    
    if (image) {
        NSLog(@"加载成功");
    } else {
        NSLog(@"加载失败");
    }


这样好麻烦,我每次都得去进行判断,这个重复的代码,程序猿不做重复的事情的

  • 没有远见的方法
// 把上面的那个代码封装起来一个方法中,比如```myImageName```这个方法

   UIImage *image = [UIImage myImageName:name];
   
// 虽然这样ok了,看着挺成功,但是如果你的大佬跟你说,去维护一个项目,把这个项目的imageNamed都输出成功不成功,那么是不是都得改啊,改成myImageName调用,哎呦,还是很麻烦


  • 可以想到,但没法实现的方法 - 重写imageNamed方法
// 尝试分类中重写系统方法
#import "UIImage+Exchange.h"

@implementation UIImage (Exchange)

+ (UIImage *)imageNamed:(NSString *)name {
    
    [self imageNamed:name]; // 死循环不行
    [super imageNamed:name]; // 父类是NSObject,没有这个方法
    
    // 总结:没法在分类中重写系统方法
    
}

  • 最好的方法 - runtime交换方法

1.runtime的API

  • class_getClassMethod 获取指定方法的地址
  • method_exchangeImplementations交换两个方法指向的地址

分类代码,看下我的源码比较清晰一点


//
//  UIImage+Exchange.m
//  RuntimeDemo
//
//  Created by user on 2017/6/8.
//  Copyright © 2017年 陈泽槟. All rights reserved.
//

#import "UIImage+Exchange.h"
#import <objc/message.h>

@implementation UIImage (Exchange)

#pragma mark - 没有在分类中重写系统方法
//+ (UIImage *)imageNamed:(NSString *)name {
//    
//    [self imageNamed:name]; // 死循环不行
//    [super imageNamed:name]; // 父类是NSObject,没有这个方法
//    
//    // 总结:没法在分类中重写父类的方法
//    
//}
//

#pragma mark - 把类加载进内存的时候调用,只会调用一次
+ (void)load {
    
    // self : 获取哪个类的方法
    //@selector(imageNamed:) : 获取imageNamed方法的地址
    Method imageName =  class_getClassMethod(self, @selector(imageNamed:));
    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    
    // 交换两个方法的地址
    method_exchangeImplementations(imageName, imageWithName);
    
    
}


#pragma mark - 这个方法也是类加载进内存调用,但是会调用多次

+ (void)initialize {
    // 解决调用多次的方法
//    static dispatch_once_t onceToken;
//    dispatch_once(&onceToken, ^{
//        <#code#>
//    });
//    
}

#pragma  mark - 实现加载图片成功后的输出
+ (UIImage *)imageWithName:(NSString *)name {
    
    UIImage *image = [UIImage imageWithName:name];
    
    if (image) {
        NSLog(@"成功");
    }else {
        NSLog(@"失败");
    }
    return image;
    
}

@end

主类调用

    // 3.交换方法
    UIImage *image = [UIImage imageNamed:@"流程图.png"];
    
    // 这时候就会输出成功了

4、动态添加方法(看源码)

  • 动态添加方法:oc都是懒加载机制(用到才去加载),只要一个方法实现了就会马上添加到方法列表中,那么添加多了就消耗了性能了

  • app 免费版,收费版的功能是不一样的,那么比如收费版的就可以是动态添加的一些方法,比如说你买了会员,就可以用某个功能,那么这个功能的方法就应该是动态添加的

  • 用到的API

1.performSelector
2.resolveInstanceMethod

// 什么时候调用:只要一个对象调用了一个未使用的实例方法就会调用这个方法进行处理

// 作用:动态添加方法,处理未实现
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == @selector(eat)) {
        // 动态添加eat方法

        // 第一个参数:给哪个类添加方法
        // 第二个参数:添加方法的方法编号
        // 第三个参数:添加方法的函数实现(函数地址)
        // 第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd
        class_addMethod(self, @selector(eat), eat, "v@:");

    }

    return [super resolveInstanceMethod:sel];

}

每个方法都有隐式的两个参数

_cmd : 当前方法的编号

self :

5.动态添加属性

  • 什么时候需要动态添加属性

    需求:让一个nsobject类,添加一个name属性,这个属性用来保存一个字符串

    runtime一般都是针对系统类

    本质:动态添加属性,就是让某个属性和对象产生关联

    如果在分类@property中添加属性?

    分类中是不能添加属性的,只会生成get、set方法声明,不会生成实现,也不会生成下划线成员属性(分类中是没有属性的)

  • 动态添加属性用到的方法

添加属性就是把这个属性跟这个类产生关联

  - (void)setName:(NSString *)name {
    // 第一个参数:给哪个对象添加属性
    // 第二个参数:属性名称
    // 第三个参数:属性值
    // 第四个参数:保存策略
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

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

推荐阅读更多精彩内容

  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,697评论 7 64
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 001 瓦拉赫效应 培养孩子不应该评价其聪明不聪明,而应该找出其聪明得地方发挥其优势。 002 罗森塔尔效应 经常...
    草木有心阅读 227评论 4 7
  • 前几晚在喜马拉雅听到一篇文章《坚持写作的意义》,如今若有所思,平时看过一些公众号的文章,里面文字是走心的,但时间久...
    张大平阅读 325评论 2 3
  • 文/詹木子 时光任然,不知不觉便稍纵即逝,犹如白驹过隙。转眼到了2017年,新的一年,新的开始,新年气象新全新的自...
    詹木子_阅读 1,010评论 0 3