手把手带你撸一个 YYModel 的精简版

515 查看

读完这篇文章你可以自己写一个 YYModel 这样的神器,这篇文章类似一个源码解析,但不同的是,它不光光是解析,更是实战,因为我觉得学习一个东西必须要自己写一遍才算是真的学了一遍,否则即便是读完了源码印象还是不会太深刻,so,开始吧。

注:为了简单起见,我的例子只是实现了一个精简的版本,YYModel 有很多功能,我这里就实现了一个核心的功能,JSON -> Model

注:文章的最后有完整的代码

从JSON映射到Model的原理

想一下平时我们是怎么使用类似这样子的库的,当我们有一个JSON的时候,我们把所有JSON的字段(比如name、page)全部写成对应的类中的属性。然后库会自动把你JSON对应字段的值赋值到这些对应的属性里去。属性我们用 @property 来定义,就意味着编译器会帮你生成对应的getset方法,我们使用的 . 其实也是在调用getset方法来实现赋值的。在 Objective-C 中有一个著名的函数 objc_msgSend(...) 我们所有的类似 [obj method] 的方法调用(发送消息)都会被转换成 objc_msgSend(...) 的方式来调用。(具体这个函数怎么用后面再说)

所以对于一个库来说,要调用你这个 Model 的 set 方法,用 objc_msgSend(...) 会容易的多,所以JSON映射到Model的原理其实就是调用这个函数而已。

所以整个流程就是,你给我一个 Model 类,我会用 runtime 提供的各种函数来拿到你所有的属性和对应的getset,判断完相应的类型以后,调用objc_msgSend(…)。说起来真的非常简单,做起来就有点麻烦…

前期的准备工作

为了后面的方便,我们需要把一些关键的东西封装起来,我们需要单独封装 ivar property method,也就是实例变量、属性、方法,但事实上我们的这个精简版的YYModel并不需要 method ivar 的封装,为了保证完整性,我还是打算写下来。

封装 ivar

先来封装 ivar,看一下头文件 CPClassIvarInfo.h(YYModel只有4个文件,两个 .h 两个 .m 我为了让代码看起来更清楚,所以我自己在重写 YYModel 的时候把所有可以拆出来的类都分别拆成了一对.h .m)并把前缀改成了 CP 意思是 copy。

Ivar 代表一个实例变量,你所有和实例变量有关的操作都必须要把 Ivar 传进去,等一下就能看到。

name 是这个实例变量的变量名

typeEncoding 是对类型的编码,具体可以看这里 对于不同的类型就会有对应的编码,比如 int 就会变编码成 i,可以用 @encode(int)这样的操作来看一个类型的编码。

type 是一个自定义的枚举,它描述了 YYMode 规定的类型。

一个强大的枚举

然后重新再创建一个文件(CPCommon),作为一个公共的文件 CPEncodingType 这个枚举就写在这里。

我们要创建的这个枚举需要一口气表示三种不同的类型,一种用于普通的类型上(int double object),一种用来表示关键词(const),一种表示 Property 的属性(Nonatomic weak retain)。

我们可以用位运算符来搞定这三种类型,用8位的枚举值来表示第一种,16位的表示第二种,24位的表示第三种,然后为了区别这三种类型都属于多少位的,我们可以分别搞三个 mask ,做一个该类型和某一个 mask 的与(&)的操作就可以知道这个类型是具体是哪一个类型了,例子在后面。

这个枚举我们可以这样定义:

比如有一个类型是这样的

假设我们并不知道它是 CPEncodingTypeDouble 类型,那我们要怎么样才能知道它是什么类型呢?只要这样:

输出: 12

在枚举的定义中

假设这个枚举值有很多种混在一起

可能有人知道这种神奇的用法,但在我读YYModel之前我没用过这种方法(技术比较菜)。

然后还有一个函数,这个函数可以把类型编码(Type Encoding)转换成刚才的枚举值,很简单却很长的一个函数: