利用 Runtime(运行时)讲解字典转模型的核心算法

本人有若干成套学习视频, 可试看! 可试看! 可试看, 重要的事情说三遍 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

运行时(runtime)是一种面向对象的编程语言的运行环境
OC 最主要的特点就是在程序运行时, 以发送消息的方式调用方法
运行时时 OC 的核心, OC 就是基于运行时的

上面的话太抽象了, 运行时能干什么? 这才是最关键的, 当我们老是被问到 YYModel 是怎么实现的时候, 一脸懵逼, 其实,在 YYModel 内部就运用了运行时, 来字典转模型,具体的思路是这样的


字典转模型的核心算法思路

以往, 我们字典转模型,总是需要在模型类中定义一个静态方法或者对象方法,来字典转模型, 这样, 我们在不同的模型中, 都必须定义这样一个方法来完成字典转模型, 如果我们写的项目比较大, 模型比较多,这样字典转模型的效率就太低了,耦合性也比较高, 那我们如何做到字典转模型 与 模型类的彻底解耦呢?

我们可以创建一个 NSObject 的分类, 因为所有的类(NSProxy 除外)都继承自 NSObject, 那我们就可以用任意的类去调 NSObject 的这个分类方法, 子类可以任意调用父类方法嘛

那么我们如何在这个分类方法中完成字典转模型呢?

这里就要用到运行时的概念了,

首先我们在分类中导入 <objc/runtime.h>这个框架, 然后进行第一步,获取属性列表
const char *kPropertyListKey = "YFPropertyListKey";

+ (NSArray *)yf_objcProperties
{
     /* 获取关联对象 */
    NSArray *ptyList = objc_getAssociatedObject(self, kPropertyListKey);
    
     /* 如果 ptyList 有值,直接返回 */
    if (ptyList) {
        return ptyList;
    }
     /* 调用运行时方法, 取得类的属性列表 */
    /* 成员变量:
     * class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 方法:
     * class_copyMethodList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 属性:
     * class_copyPropertyList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 协议:
     * class_copyProtocolList(__unsafe_unretained Class cls, unsigned int *outCount)
     */
    unsigned int outCount = 0;
    /**
     * 参数1: 要获取得类
     * 参数2: 雷属性的个数指针
     * 返回值: 所有属性的数组, C 语言中,数组的名字,就是指向第一个元素的地址
     */
    /* retain, creat, copy 需要release */
    objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
    
    NSMutableArray *mtArray = [NSMutableArray array];
    
     /* 遍历所有属性 */
    for (unsigned int i = 0; i < outCount; i++) {
         /* 从数组中取得属性 */
        objc_property_t property = propertyList[i];
         /* 从 property 中获得属性名称 */
        const char *propertyName_C = property_getName(property);
         /* 将 C 字符串转化成 OC 字符串 */
        NSString *propertyName_OC = [NSString stringWithCString:propertyName_C encoding:NSUTF8StringEncoding];
        [mtArray addObject:propertyName_OC];
    }
    
     /* 设置关联对象 */
    /**
     *  参数1 : 对象self
     *  参数2 : 动态添加属性的 key
     *  参数3 : 动态添加属性值
     *  参数4 : 对象的引用关系
     */

    objc_setAssociatedObject(self, kPropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    /* 释放 */
    free(propertyList);
    return mtArray.copy;
    
}
其实上面这一长串代码中,只有4句是最关键的

1./* 获取关联对象 */ NSArray *ptyList = objc_getAssociatedObject(self, kPropertyListKey);
如果在程序运行的时候, 模型对象的属性是不会发生变化的, 我们在利用这个函数如果能获取到关联对象的属性列表, 就不用再走下面的代码去利用运行时再去获取属性列表了

2.objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
这句代码就是真正的利用运行时获取属性列表, 这个属性列表是 C 的结构体指针数组,我们必须将其遍历,并利用另外一个函数将取出结构体指针所指向的结构体中国的 C 字符串,也就是属性名称

3.const char *propertyName_C = property_getName(property);
获得C字符串后,我们只需要将其转换为 OC 字符串,加到可变数组中即可

4.objc_setAssociatedObject(self, kPropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
设置属性列表, 就是把已经生成好的属性列表设置到一个类似于属性的东西储存起来, 下次 get 的时候,直接拿出来用即可,有点类似于懒加载.

获取属性列表之后, 我们就要进行字典转模型的操作了

首先我们要遍历参数字典, 如果我们获取得属性列表中包含了字典中的 key,就利用 KVC 方法赋值,然后就完成了字典转模型的操作

+ (instancetype)yf_objcWithDict:(NSDictionary *)dict
{
     /* 实例化对象 */
    id objc = [[self alloc]init];
    
     /* 使用字典,设置对象信息 */
     /* 1. 获得 self 的属性列表 */
    NSArray *propertyList = [self  yf_objcProperties];
    
     /* 2. 遍历字典 */
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
         /* 3. 判断 key 是否字 propertyList 中 */
        if ([propertyList containsObject:key]) {
             /* 说明属性存在,可以使用 KVC 设置数值 */
            [objc setValue:obj forKey:key];
        }
        
    }];
    
     /* 返回对象 */
    return objc;
}

这样, 比如我在 ViewDidLoad 方法中, 自定义一个字典
然后我只需要一行代码就可以获取到模型对象,如下

- (void)viewDidLoad {
    [super viewDidLoad];
    /* 创建一个字典 */
    NSDictionary *dict = @{
                           @"name":@"小明",
                           @"age":@18,
                           @"title":@"master",
                           @"height":@1.7,
                           @"something":@"nothing"
                           };
    
    Person *person = [Person yf_objcWithDict:dict];
}

而此时, 模型类中,没有添加任何的构造方法,只有单纯的属性,这样就做到了彻底的解耦, 比如我现在再来一个学生(Student)类,我也无需添加构造方法,也同样只需要调用-(instancetype)yf_objcWithDict:dict;即可.

模型类只有单纯的属性
一行代码完成字典转模型
这就是一些第三方框架,例如 YYModel,MJExtension等的核心算法,当然,他们会做很多优化, 因为实际上的字典转模型是很复杂的, 字典里嵌套数组,数组里有数组,数组里有字典,字典里又有数组等等, 这我就 hold不住啦,感谢各位捧场!

PS. 本人有若干成套学习视频, 包含Java, 数据结构与算法, iOS, 安卓, python, flutter等等, 如有需要, 联系微信tsaievan.

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

推荐阅读更多精彩内容