蜜蜂浅谈Runtime Part1(菜鸟级别)

本人是渣渣一枚,最近浅浅的接触了一下runtime,给大家分享一些我的理解,高手勿喷

runtime简称运行时,采用C和汇编写的,是苹果为了动态系统的高效而做出的努力. OC从三个不同的层级上与runtime进行交互,分别为:

1.Object-C源代码

2.Foundation框架和NSObject类定义的方法

3.Runtime函数的直接使用

在平时的开发中我们只需要写OC代码,而runtime会自动在幕后运作

1.RunTime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。

2.对于C语言,函数的调用在编译的时候会决定调用哪个函数,编译完成之后直接顺序执行,无任何二义性。

3.OC的函数调用成为消息发送。属于动态调用过程。在编译的时候并不能决定真正调用哪个函数(事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错)。

只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

相信很多人听了概念,只有个大概的印象,runtime,其实最多的运用于model,还有交换/增加/跟踪方法,我写了几个demo助于各位理解

Model实现NSCoding的自动归档和解档
按照我之前比较入门级的写法,我们需要对model的每一个属性都实现一遍encodeObject和decodeObjectForKey方法,那如果有多个属性,我们则必须一个个的写一遍,在这里我们就可以采用runtime来渐变实现

这里我先看一下runtime的一个获取变量列表的方法

#pragma --- 这里先介绍两个需要涉及到的runtime的两个知识 ---
    // * 获取属性列表
    /**
        这里count可以通过 class_copyPropertyList 这个方法得到属性的数量
        [self class] 指的是你想要获取的那个类,如果想获取BeeEncodeModel的属性则可以改成[BeeEncodeModel class]
        这时候我们propertylist能够获取到类中的所有成员属性,然后根据count就可以取到相对应的属性名称
    */
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"类中包含属性:%@", [NSString stringWithUTF8String:propertyName]);
    }
    
    
    // * 获取成员变量
    /**
        这个方法跟上面的大同小异,就是方法改为 class_copyPropertyList
        class_copyIvarList:获取类中的所有成员属性
        Ivar:成员属性的意思
        然后这个方法是可以获取得到成员变量,我们通过打印就可以很清楚的看到
    */
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"类中包含成员变量%@", [NSString stringWithUTF8String:ivarName]);
    }

这里要注意一下,获取成员变量的方法因为是用指针指向元素的方法,所以得到的变量名字前面会有一个"",属性列表的方法就没有"",这里我们可以在控制台清楚的看出来

有了获取成员变量这个方法,我们可以动态的控制类的属性,方法如下:

#pragma mark - 通过动态变量控制修改BeeEncodeModel的age属性,把age的值改成20
- (void)change_variable{
//    _beeModel = [[BeeEncodeModel alloc]init];
    unsigned int count = 0;
    Ivar *ivar = class_copyIvarList([_beeModel class], &count);
    for (int i = 0; i<count; i++) {
        Ivar var = ivar[i];
        const char  *varName = ivar_getName(var);
        NSString *name = [NSString stringWithUTF8String:varName];
        if ([name isEqualToString:@"_age"]) {
            
// * 在这里也可以把Garlic.h的age为nsnumber类型,则:
            object_setIvar(_beeModel, var, @20);
// * 如果是int等基本数据类型,则可以使用强转
//            object_setIvar(beeVC, var, (__bridge id)((void *)20));
            
            break;
        }
    }
    
    NSLog(@"beeVC age is %@",_beeModel.age);
}

接下来我们创建一个BeeEncodeModel,我们是用runtime的方法对这个model进行NSCoding的自动归档和解档,同样的,也是用到了encodeObject和decodeObjectForKey方法

#import <Foundation/Foundation.h>

@interface BeeEncodeModel : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, assign) NSNumber *age;

// model 的成员变量不需要全部都存在于字典中
@property (nonatomic, copy) NSString *name1;
@property (nonatomic, copy) NSString *address1;
@property (nonatomic, assign) NSNumber *age1;

@end


#import "BeeEncodeModel.h"

#import <objc/runtime.h>

@implementation BeeEncodeModel

/**
    我们通过获取runtime的方法来获取BeeEncodeModel的成员属性,可以得到这个类所有的成员属性,然后遍历一下进行归档,我们就不用一个个去写了
    这里使用的是获取的成员变量方法,所以会有 "_" ,因此我们通过 key = [key substringFromIndex:1] 来删除 "_",使得这个key能跟成员属性匹配从而进行归档
    所以我们只需要在.h文件中写上成员属性,而.m的代码是不用动的.就已经能实现自动归档和解档
*/

- (void)encodeWithCorder
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([BeeEncodeModel class], &count);
    for (int i = 0; i < count; i++) {
        // 取出i对应的成员变量
        Ivar ivar = ivars[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        key = [key substringFromIndex:1];
        // 设置到成员变量上
        [self setValue:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCorder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([BeeEncodeModel class], &count);
        for (int i = 0; i<count; i++) {
            // 取出i位置对应的成员变量
            Ivar ivar = ivars[i];
            // 查看成员变量
            const char *name = ivar_getName(ivar);
            // 归档
            NSString *key = [NSString stringWithUTF8String:name];
            key = [key substringFromIndex:1];
            id value = [decoder decodeObjectForKey:key];
            // 设置到成员变量身上
            [self setValue:value forKey:key];
        }
        free(ivars);
    }
    return self;
}

@end

这就是运用了runtime的方法把属性都遍历了出来然后一个个进行归档和解档,就不用我们一个个去写了.

当然,我觉得真正强大的并不是在这里,我们可以在model自定义一个方法

可能我们访问后台拿到的json数据,就是一个大字典,但是可能大字典里有数组或者有小字典,这时我们可以用runtime的方式,自定义一个方法可以让这些子字典给弄出来,即一个字典里面如果包含字典或者数组,也可以提取里面相对应的key和value直接使用,即让成员属性可以跟每个key相对应,话不多说,上代码

#import "GarlicModel.h"

#import <objc/runtime.h>

@implementation GarlicModel

/**
    这个model不需要归档和解档,直接写一个类方法
    这个model可以让一个字典里面如果包含字典或者数组,也可以提取里面相对应的key和value直接使用,即让成员属性可以跟每个key相对应
*/

+ (instancetype)modelWithDict:(NSDictionary *)dict
{
    // 创建对应模型对象
    id objc = [[self alloc] init];
    
    unsigned int count = 0;
    
    // 1.获取成员属性数组
    Ivar *ivarList = class_copyIvarList(self, &count);
    
    // 2.遍历所有的成员属性名,一个一个去字典中取出对应的value给模型属性赋值
    for (int i = 0; i < count; i++) {
        
        // 2.1 获取成员属性
        Ivar ivar = ivarList[i];
        
        // 2.2 获取成员属性名 C -> OC 字符串
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        // 2.3 _成员属性名 => 字典key
        NSString *key = [ivarName substringFromIndex:1];
        
        // 2.4 去字典中取出对应value给模型属性赋值
        id value = dict[key];
        
        // 获取成员属性类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        
        // 这里要注意判断value,因为model的成员属性不一定存在于字典中,若不存在,value为null
        if (value) {
            NSLog(@"value = %@,key = %@",value,key);
            [objc setValue:value forKey:key];
        }
        
        
        // * 当字典里嵌套字典的时候
        //判断如果model里有字典
        if ([value isKindOfClass:[NSDictionary class]] && [ivarType containsString:@"NS"]) {
            
            //  是字典对象,并且属性名对应类型是自定义类型
            // 处理类型字符串 @\"User\" -> User
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            // 自定义对象,并且值是字典
            // value:user字典 -> User模型
            // 获取模型(user)类对象
            Class modalClass = NSClassFromString(ivarType);
            
            // 字典转模型
            if (modalClass) {
                [objc objectWithDict:value and:objc];
            }
            
        }
        //* 当字典里嵌套数组的时候
        //判断如果model有数组
        if ([value isKindOfClass:[NSArray class]] && [ivarType containsString:@"NS"]){
            
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            Class modalClass = NSClassFromString(ivarType);
            if (modalClass) {
                [objc objectWithArray:value and:objc];
            }
        }
        
    }
    return objc;
}

- (void)objectWithDict:(NSDictionary *)dict and:(id)objc
{
    
    unsigned int count = 0;
    
    // 1.获取成员属性数组
    Ivar *ivarList = class_copyIvarList([GarlicModel class], &count);
    
    for (int i = 0; i<count; i++) {
        // 取出i位置对应的成员变量
        Ivar ivar = ivarList[i];
        // 查看成员变量
        const char *name = ivar_getName(ivar);
        // 归档
        NSString *key = [NSString stringWithUTF8String:name];
        key = [key substringFromIndex:1];
        
        id value = dict[key];
        
        // 设置到成员变量身上
        if (value) {
            [objc setValue:value forKey:key];
        }
    }
    
    
}

- (void)objectWithArray:(NSArray *)array and:(id)objc
{
    for (int a = 0; a < array.count; a ++) {
        unsigned int count = 0;
        
        NSDictionary *dict = array[a];
        
        // 1.获取成员属性数组
        Ivar *ivarList = class_copyIvarList([GarlicModel class], &count);
        
        for (int i = 0; i<count; i++) {
            // 取出i位置对应的成员变量
            Ivar ivar = ivarList[i];
            // 查看成员变量
            const char *name = ivar_getName(ivar);
            // 归档
            NSString *key = [NSString stringWithUTF8String:name];
            key = [key substringFromIndex:1];
            
            id value = dict[key];
            
            // 设置到成员变量身上
            if (value) {
                [objc setValue:value forKey:key];
            }
        }
    }
}

@end

这样子每一个数据里每一个key都会和属性对应起来,无论是存在字典中的数组还是字典中的字典.

便于大家理解,我也写了个小demo网上
https://github.com/iOSJYF/Garlic_runtime/tree/master/Garlic_WithRuntime
第一次写文章,大家笑纳哈~~

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

推荐阅读更多精彩内容

  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,700评论 7 64
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,690评论 0 9
  • 目录 Objective-C Runtime到底是什么 Objective-C的元素认知 Runtime详解 应用...
    Ryan___阅读 1,935评论 1 3
  • 闭眼和睁眼之间,恍若已经隔了一个世纪,时间过得真快,转眼之间一个月就过去,来深圳已经一个多月了,这一个月里面,我经...
    当时一样雨阅读 177评论 0 0
  • ——来自官网(澳大利亚驻华大使馆) 澳大利亚政府高兴地宣布澳大利亚已同中国达成协议对中国公民开放打工度假签证。这种...
    PascalSun阅读 667评论 0 1