YYModel源码阅读笔记(一)

model和json转换时开发中普遍使用的,在iOS开发中系统KVO本身也提供了简单的API:

同时常用流行的model和json的转化框架有MJExtension、Mantle、JSONModel、YYModel等。今天主要就YYModel得源码做一个剖析,延续之前风格,还是通过一个典型的应用展开分析。

首先让我们看一下这个框架的基本结构:

可以看到这是一个很轻量简洁的工具,只有四个文件,其中NSObject+YYModel是NSObject的categary提供了相应的使用接口,和Mantle相比没有侵入性,YYClassInfo类是针对NSObject的Ivar、Property、Method进行封装的类,之后具体分析。

下边先看一个调用:

以上是YYKit的demo中一个简单实例。

之下就从json转model的API开始分析:

NSDictionary *dic = [self _yy_dictionaryWithJSON:json]可以看出,由于传入的jsonid类型,所以首先要将其转化为NSDictionary*类型,然后就变了字典转模型。先具体分析一下这个将json转化为字典的方法:

可看出如果json为空,直接返回的dic也是空,如果json是字典直接返回,如果是字符串则调用系统方法jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding]将其转化为NSData*类型的jsonData,如果jsonNSData*类型则直接将json返给jsonData,然后调用系统方法dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL]jsonData转化为字典dic并返回。

接下来看这个将字典转化为model的方法:[self modelWithDictionary:dic]

如果传入参数dictionary为空或不是字典,返nil。

Class cls = [self class]返回类本身。

_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls]创建了一个_YYModelMeta*类型的对象modelMeta

接下来就从这个初始化工厂方法入手,详细分析_YYModelMeta这个类:

方法前半部分说明,cache是在程序运行期间只生成一次的静态变量,lock在这里是用信号量实现的安全锁,从_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls))可以看出cache是在程序运行期间缓存每个cls对应的_YYModelMeta *类型的meta对象。

所以这个方法就是如果全局还没有cache,则创建cache,并创建安全锁lock,然后从cache中获取以clskey值对应的_YYModelMeta*类型的对象,如果没有或对象需要更新则调用meta = [[_YYModelMeta alloc] initWithClass:cls]创建meta对象,并插入到cache中,整个过程通过lock锁保证了线程安全。

在深入探讨具体的初始化成员方法:

可以看出这是一个很庞大的方法。YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls]这一步是闯将一个YYClassInfo*类型的对象classInfo,开篇提到过YYClassInfo是用来存储一些类信息的,现在就具体深入到这个方法中:

类似于_YYModelMeta的工厂方法,先在缓存中查YYClassInfo *info,如果没有,则调用info = [[YYClassInfo alloc] initWithClass:cls]方法创建,创建完后将其存入相应的缓存中(classCachemetaCache)。

再深入探查YYClassInfo的具体初始化方法:

在这个方法中依次保存了@property (nonatomic, assign, readonly) Class cls@property (nullable, nonatomic, assign, readonly) Class superCls@property (nullable, nonatomic, assign, readonly) Class metaCls@property (nonatomic, strong, readonly) NSString *name这些属性。

再看[self _update]这个方法做了什么:

可以看出这个方法主要是为@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos这三个属性赋值。

其中_ivarInfos存储了类的所有成员变量,_methodInfos类的所有成员方法,_propertyInfos存储了类的所有属性。

_methodInfos为类:

先是通过runtime方法Method*methods =class_copyMethodList(cls, &methodCount)获取类的所有方法,然后遍历方法,针对每个方法创建一个YYClassMethodInfo *类型的info:

可以看出YYClassMethodInfo保存了方法名、方法选择子、函数指针、参数类型等,然后调用方法if(info.name) methodInfos[info.name] = info以方法名字为key值,以YYClassMethodInfo *infovalue值,将所有方法存进_methodInfos字典中。_ivarInfos_propertyInfos类似,也都是用字典存储了所有的属性和成员变量。最后设置_needUpdate = NO。

再回到YYClassInfo- (instancetype)initWithClass:(Class)cls方法中:_superClassInfo = [self.class classInfoWithClass:_superCls]再依次创建模型父类的YYClassInfo*类型的对象并保存在缓存中。

接着回到_YYModelMeta的- (instancetype)initWithClass:(Class)cls方法中。

统计黑名单和白名单。

这一步是获取容器属性中的类型信息。

这一步遍历之前生成的propertyInfos,先根据黑名单和白名单做相应的过滤,针对需要在模型转化中应用的属性进行一个_YYModelPropertyMeta*类型的封装,然后保存在allPropertyMetas中,并将allPropertyMetas字典中的所有value值赋给_allPropertyMetas属性。

如果模型自定义的属性和字典的key值不一致的话,这一步是建立一个影射(需要在模型中实现modelCustomPropertyMapper方法,在这个方法中返回映射字典)。然后将相关影射信息存在_mapper属性中。


最后对_YYModelMeta*类型的meta进行赋值,然后返回。

就此,_YYModelMeta类的- (instancetype)initWithClass:(Class)cls方法结束。

返回到+ (instancetype)metaWithClass:(Class)cls方法中:

_YYModelMeta*meta放入缓存中,这个方法也结束。

经过如此繁杂的操作,你是否已经迷路了呢,现在回到字典转模型这一步了:

上面的操作就是_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls]这一步,这一步就是将模型类的相关属性、成员变量、方法、黑名单、白名单、模型属性和字典索引之间影射等信息取出来并全局缓存,在下次调用的时候直接获取可以大大提高运行速度。

下面就开始分析将字典转化为模型的方法:

先创建一个模型对象,再通过调用方法[one modelSetWithDictionary:dictionary]给对象赋值:

在这个方法中调用CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,  CFRangeMake(0, modelMeta->_keyMappedCount), ModelSetWithPropertyMetaArrayFunction,  &context)方法赋值。

CFArrayApplyFunction对第一个数组类型的参数在第二个CFRange类型参数区间中遍历,没遍历一次调用一次第三个函数指针类型的参数指向的函数,第四个参数为调用的函数的第二个参数,每遍历一次的数组元素为函数的第一个参数。

可以看出第三个参数指向的函数式具体的对每一个属性赋值的函数,可展开来看:

就此,回到:

整个字典转模型结束。

之上就是一个字典转模型的主干过程,整个过程还有很多细节都很赞,整个框架在安全性和效率性兼顾做的确实很棒,对于一些细节,后边再继续探索。

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

推荐阅读更多精彩内容

  • 柳天残是古装武侠剧《八大豪侠》中的男性角色。他是世间少有的武学奇才,可以同时运刀使剑,俨如天上神兵。 柳天残自创了...
    柳育龙阅读 165评论 0 0
  • 所谓恶习(è xí),就是坏习惯,每个人都有恶习!吸烟、喝酒、打麻将、赌博、花心、玩游戏等等都是或大或小,...
    淡定宝宝阅读 701评论 1 0
  • 身为微商小白,当我们刚进入微商行业,常常是满腔热血地到处加好友。但好友加了一堆,更多的只是寻问并不购买,产品始终卖...
    微小咖阅读 279评论 0 0
  • 翻译成《再次出发之纽约遇见你》真是够了…… 话说这海报上真的看不出来是KK的侧脸。 这个就切题多了,虽然不是很有特...
    水御龙神阅读 391评论 1 1
  • 醒来的不合时宜,明明说今天会有雨,以为气温会降低一些,还是被热醒。这个夏天的炎热,空调好像都无济于事。再入梦却不找...
    风阿尘阅读 101评论 0 1