RunTime

runtime是什么?
  • runtime又称运行时,也就是运行时候的一些机制,其中最总要的就是消息机制.
  • 任何调用方法的本质都是发送消息

oc 与c 有什么不同?
  • c语言函数,在编译阶段就确定调用哪个函数
  • oc方法,在编译的时候并不能决定调用哪个方法,只有在真正运行的时候才会根据方法名称找到与之对应的函数实现
    *注意:对于oc 来说是调用方法,对于c来说是调用函数.方法和函数是有区别的,对于oc根据方法名去找到函数名,相当于函数实现是方法的实现.函数名是函数实现的入口
  • 在编译阶段,oc 中的方法只要声明就不会报错,而c中的函数必须实现,才不会报错.

runtime发消息
NSObject *p = [NSObject alloc];
p = [p init];

clang -rewrite-objc ViewController.m
如果报错看这里http://www.jianshu.com/p/43a09727eb2c
这句话将ViewController.m代买转化成ViewController.cpp,oc代码转成c++代码
然后我们去ViewController.cpp看看6万多行代码,
搜索@implementation ViewController

 NSObject *p = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));
 p = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("init"));

精简一下:

 NSObject *p = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));
 p = objc_msgSend(p, sel_registerName("init"));

从上面代码我们看到,我们创建对象的本质其实就是发消息
objc_msgSend:发消息
objc_getClass : 获取类
sel_registerName:注册方法编号,相当于@selector()


runtime几种用途
  • 方法交换

举个例子,比如[UIImage imageNamed:@"1"],那么这张图片有没有也不知道,那么我们该怎么做呢?

  1. 可以建一个自定义的类,每次调用自己的类,但是这样每次都需要导入头文件,而且如果项目已经维护了好久,那么这种方法会改起来没麻烦
  2. 可以写个类别,但是问题同上

因此,这时候我们需要想到runtime方法交换,因为我们不想改动之前的代码,但是之前的代码不符合我们需求,我们需要扩展,所以我们应该想到这种方法.
那么需要将imageNamed系统方法实现IMP 和 自定义的 gl_ imageNamedIMP 交换

 //新建一个类别
//值调用一次,在加载类的时候调用
+ (void)load{
    
    Method method1 = class_getClassMethod([self class], @selector(imageNamed:));
    Method method2 = class_getClassMethod([self class], @selector(gl_imageNamed:));

    
    method_exchangeImplementations(method1, method2);
    
}

//会调用多次,可以用单利来限制代码执行次数,因为swift里没有+load,所以只能用+initialize
+ (void)initialize{
    
}

+ (UIImage *)gl_imageNamed:(NSString *)name{

    UIImage *image = [self gl_imageNamed:name];
    
    if (image) {
        NSLog(@"图片存在");
    } else {
        NSLog(@"图片不存在");
    }
    
    return image;
}

其中我说明了load方法和initialize 的区别
这样我在调用UIImage *image = [UIImage imageNamed:@"qq"];会告诉我图片没有


  • 动态添加方法

1.方法调用流程:
1)通过isa 指针去对应的类中去找方法,对象方法去类对象的方法列表去找方法,类方法去元类的方法列表中去找方法.
2)注册方法编号
3)根据方法编号去查找方法
4)找到最终函数实现地址

2.运行时添加一个方法,我再举个🌰:
我建一个Person类,接着调用

Person *p = [[Person alloc] init];
[p performSelector:@selector(eat)];

为什么用performSelector,因为对于没有声明的方法,编译时无法编过去, performSelector是运行时执行,接着运行一下,crash!
reason: '-[Person eat]: unrecognized selector sent to instance 0x604000002220'
我们可以拦截这个崩溃,在Person.m里添加

void eat(id self,SEL _cmd){
    NSLog(@"吃了");
}

//第一次拦截,其实有三次转发
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if ([NSStringFromSelector(sel) isEqualToString:@"eat"]) {
        class_addMethod([self class], sel, (IMP)eat, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

class_addMethod 方法最后一参数在苹果文档中查找
v:代表返回值是void
@:代表参数是id
::代表参数是 sel
void eat(id self,SEL _cmd){ NSLog(@"吃了"); }:该函数的两个参数,是默认传的,其实苹果每个函数调用都会传self和_cmd,只是编译器帮我们做了.

如果我们使用[p performSelector:@selector(eat) withObject:@1];带参数的,对应的写成下面这个样子的

void eat(id self,SEL _cmd,NSNumber *n){
    NSLog(@"吃了%@",n);
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if ([NSStringFromSelector(sel) isEqualToString:@"eat"]) {
        class_addMethod([self class], sel, (IMP)eat, "v@:@");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

  • 动态添加属性

本质也就是类别中添加一个属性,
因为类别中只会set get 方法声明,不会实现,也不会生成对应的下划线成员变量.看看代码

@interface Person : NSObject

@property NSString *name;

@end

由于方法没有实现,所以给属性设置策略是无用的

- (void)setName:(NSString *)name{
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)name{
    return  objc_getAssociatedObject(self, @"name");
}

将该属性关联到Person对象上


  • 字典转模型

重中之重!
好多解析model 的框架底层原理都是基于runtime

1.动态添加属性代码

{
    "classNum": "3年二班",
    "user": [
             {"name":"张三"
             "age":22
             },
             {"name":"李四"
             "age":34
             },
             ]
    "isHave":YES
    "totol":2
}

建一个NSDictionary 分类

- (void)createPropertyWithDict{
    NSMutableString *string = [NSMutableString string];

    [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        
        
        if ([obj isKindOfClass:[NSString class]]) {
            NSString* str = [NSString stringWithFormat:@"@property (nonatomic,strong) NSString *%@;",key];
            [string appendFormat:@"\n%@\n",str];
        }else if ([obj isKindOfClass:[NSArray class]]){
            NSString* str = [NSString stringWithFormat:@"@property (nonatomic,strong) NSArray *%@;",key];
            [string appendFormat:@"\n%@\n",str];

        }else if ([obj isKindOfClass:[NSDictionary class]]){
            NSString* str = [NSString stringWithFormat:@"@property (nonatomic,strong) NSDictionary *%@;",key];
            [string appendFormat:@"\n%@\n",str];

        }else if ([obj isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){
            NSString* str = [NSString stringWithFormat:@"@property (nonatomic,assign) BOOL %@;",key];
            [string appendFormat:@"\n%@\n",str];

        }else if ([obj isKindOfClass:[NSNumber class]]){
            NSString* str = [NSString stringWithFormat:@"@property (nonatomic,assign) NSInteger %@;",key];
            [string appendFormat:@"\n%@\n",str];
        }

    }];
    
    NSLog(@"%@",string);

}

然后用字典直接调用createPropertyWithDict
得到结果如下

@property (nonatomic,assign) NSInteger totole;

@property (nonatomic,strong) NSString * classNum;

@property (nonatomic,assign) BOOL isHave;

@property (nonatomic,strong) NSArray *user;

2.字典转model

- (id)modelWithDict:(NSDictionary*)dict{
    NSObject *obj = [[[self class] alloc] init];
    unsigned int count = 0;
    //之所以用class_copyIvarList 而不用 class_copyPropertyList,因为我们可能忽略成员变量,但不会忽略属性
    Ivar *IvalList = class_copyIvarList([self class], &count);
    for (int i = 0; i<count; i++) {
        Ivar ivar = IvalList[i];
        //ivar_getName(ivar) 得到成员变量的名字
        //c语言字符串转oc字符串转
        NSString *ivarStr = [NSString stringWithUTF8String:ivar_getName(ivar)];
        ivarStr = [ivarStr substringFromIndex:1];
        //ivar_getTypeEncoding(ivar) 得到成员变量的类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];

        [obj setValue:dict[ivarStr] forKey:ivarStr];
    }
    
    return obj;
}

以上就是关于runtime 的一个总结,很多细节部分需要自己去动手才能发现,谢谢

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

推荐阅读更多精彩内容

  • iOS开发-- Runtime的几个小例子 字数2756阅读1867评论22喜欢88 一、什么是runtime(也...
    K_Gopher阅读 367评论 0 0
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,695评论 7 64
  • 参考链接: http://www.cnblogs.com/ioshe/p/5489086.html 简介 Runt...
    乐乐的简书阅读 2,127评论 0 9
  • Runtime是什么 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我...
    SuAdrenine阅读 864评论 0 3
  • 1 同事大姐给同事娜娜介绍个对象,那人我们都认识,也是同事。说实话,还行。长相过得去,无不良嗜好,收入略高于娜娜,...
    哆啦A梦的理想阅读 453评论 0 5