YYModel源码分析

前言

YYModel 是一个iOS JSON模型转化库,和其他一些同类型库相比,具有比较好的性能优势。本文会对YYModel的源码进行分析,具体用法作者ibireme在github中有提及。YYModel的目录结构很简单,只有两个类, NSObject+YYModelYYClassInfoYYClassInfo主要对根类NSObject 的 Ivar , Method, Property以及Class本身进行了封装,NSObject+YYModel 是 NSObject的分类,扩展了一些JSON模型转化的方法。

YYClassInfo

要点

YYClassIvarInfo : 对 Class的Ivar进行了封装
YYClassMethodInfo : 对 Class的Method进行了封装
YYClassPropertyInfo : 对 Class的Property进行了封装
YYClassInfo : 对Class进行了封装,包含了YYClassIvarInfo,YYClassMethodInfo,YYClassPropertyInfo

变量类型编码

说到变量,可能我们写代码中最常见的就是变量了。当我们自定义一个类时,首先总是会申明一些属性,而且每个属性都会带有一些修饰词。比如是否原子性,内存管理原则,只读性。这些都可以通过这个属性的property_getAttributes 方法获取,苹果为所有的类型,包括属性类型都有编码,具体可以查看苹果官方文档:类型编码, 苹果官方文档:属性类型。 下面是一个简单例子:

  1. 申明属性
1
2
3
4
5
6
7
8
9
@interface Cat : NSObject
@end
@interface Person : NSObject
@property (nonatomic,copy) NSString *name;
@property (nonatomic,assign) NSInteger age;
@property (nonatomic,strong) Cat *cat;
- (NSDictionary *)getProperties;
@end

2.通过runtime 来获取所有属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "Person.h"
#import <objc/runtime.h>
@implementation Person
- (NSDictionary *) getProperties{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
unsigned int count = 0;
objc_property_t *propertyList = class_copyPropertyList([self class], &count);
for (NSUInteger i = 0; i < count; i++) {
const char *name = property_getName(propertyList[i]);
const char *attribute = property_getAttributes(propertyList[i]);
NSString *nameStr = [NSString stringWithUTF8String:name];
NSString *attributeStr = [NSString stringWithUTF8String:attribute];
dic[nameStr] = attributeStr;
}
return [dic copy];
}
@end

3.打印结果

1
2
3
4
5
2017-01-03 10:54:34.690 Properties[16941:881462] {
age = "Tq,N,V_age";
cat = "T@\"Cat\",&,N,V_cat";
name = "T@\"NSString\",C,N,V_name";
}

我们可以看到属性的所有类型编码信息,其中第一个代表是这个变量的类型,以T开头,最后一个代表的是变量的名字,一般用V_属性名表示,中间的部分就是我们声明的修饰符。比如age的类型是 Tq,而在官方文档中q 代表了A long long,64bit下NSInteger的取值范围就是long == long long ,N代表了非原子性,变量名是_age。其他的@代表了OC类型 id ,cat类型即是T@”Cat”,&代表了 这个变量是retain (ARC下strong相当于retain),C 代表了copy

YYEncodingType

根据类型编码自定义了类型枚举,包含了三个部分

YYEncodingTypeMask : 0~8位的值,变量的数据类型
YYEncodingTypeQualifierMask : 8~16位的值,变量的方法类型
YYEncodingTypePropertyMask: 16~24位的值,变量的属性类型

这里把枚举值分成三个部分,通过 枚举值 & 对应 Mask 取出对应的变量类型,区分不同类型部分。YYEncodingGetType 是根据变量的数据类型编码值获取自定义YYEncodingType

Var Method Property

YYClassIvarInfo:用于存取变量的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
Instance variable information.
*/
@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type
/**
Creates and returns an ivar info object.
@param ivar ivar opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithIvar:(Ivar)ivar;
@end

Ivar是表示实例变量的类型,其实际是一个指向objc_ivar结构体的指针,其定义如下:

1
2
3
4
5
6
7
8
struct objc_ivar {
charchar *ivar_name OBJC2_UNAVAILABLE;
charchar *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}

运用runtime,name通过的ivar_getName获取,offset 通过ivar_getOffset获取,typeEncoding 通过 ivar_getTypeEncoding 获取,type 通过自定义方法 YYEncodingGetType获取。其中offset是变量的基地址偏移量,可以通过它来直接访问变量数据,下面是例子:

1
2
3
4
5
6
7
8
Person *p = [[Person alloc]init];
// NSLog(@"%@",[p getProperties]);
p.age = 20;
Ivar age_ivar = class_getInstanceVariable([Person class], "_age");
long *age_pointer = (long *)((__bridge void *)(p) + ivar_getOffset(age_ivar));
NSLog(@"age ivar offset = %td", ivar_getOffset(age_ivar));
*age_pointer = 10;
NSLog(@"%@", p);

1
2
3
4
5
- (NSString *)description {
NSLog(@"current pointer = %p", self);
NSLog(@"age pointer = %p", &_age);
return [NSString stringWithFormat:@"age = %zi", _age];
}

打印结果:

1
2
3
4
2017-01-03 14:41:14.175 Properties[20934:1554728] age ivar offset = 16
2017-01-03 14:41:14.176 Properties[20934:1554728] current pointer = 0x600000075140
2017-01-03 14:41:14.176 Properties[20934:1554728] age pointer = 0x600000075150
2017-01-03 14:41:14.177 Properties[20934:1554728] age = 10

YYClassMethodInfo:用于存取方法的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
Method information.
*/
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< method name
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
/**
Creates and returns a method info object.
@param method method opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithMethod:(Method)method;
@end

Method 的信息同 Ivar 一样,通过runtime的 method相关方法获取,其他的一些信息同Ivar,主要来说一下 SELImp

SEL: 方法ID,C字符串

typedef struct objc_selector *SEL;

1
2
3
4
5
/// Defines a method
struct objc_method_description {
SEL name; /**< The name of the method */
char *types; /**< The types of the method arguments */
};

IMP:方法函数指针

OC是动态语言,方法调用(也叫做消息发送)是在运行时动态绑定的,而非编译时。如何做到正确的调用指定的方法呢?这里就需要用到SEL和IMP。编译器会将消息发送转换成对objc_msgSend(void /* id self, SEL op, ... */ )方法的调用 。objc_msgSend方法根据对象的isa指针找到对象的类,通过在类中的调度表(dispatch table)中查找SEL 获得 IMP,精确执行指定方法。

YYClassPropertyInfo: 用于存取属性的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
Property information.
*/
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< property's name
@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
/**
Creates and returns a property info object.
@param property property opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithProperty:(objc_property_t)property;
@end

YYClassInfo: 对Class的封装,包含了上面三部分的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
Class information for a class.
*/
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< class object
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
@property (nonatomic, strong, readonly) NSString *name; ///< class name
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties

元类

其中有metaCls代表了元类。那什么是元类呢?下面是一张经典的类结构图

isa.png

在OC中,每个实例对象都有一个isa指针,它指向了对象的class。而这个class也同样有一个isa指针,它就是指向了它的元类,其实类也是一个对象,所以对象之于类的关系,就相当于类(类对象)之于其元类(类对象的类)的关系。那元类有什么用呢?我们都知道在OC中调用方法有实例方法和类方法。我们调用实例方法,就是通过isa指针找到指定的class,查找存储在class中的方法列表执行方法,所以元类的作用就是调用类方法时,通过查找保存在元类中的类方法执行方法的作用。那为什么不把所有方法都保存在类中,可能这样更加高效也节省资源吧,具体可以自己查找资料。

在YYClassInfo中,有一个_update方法,用来更新类中存储的信息。

初始化方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
+ (instancetype)classInfoWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef classCache;
static CFMutableDictionaryRef metaCache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
if (info && info->_needUpdate) {
[info _update];
}
dispatch_semaphore_signal(lock);
if (!info) {
info = [[YYClassInfo alloc] initWithClass:cls];
if (info) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
dispatch_semaphore_signal(lock);
}
}
return info;
}

要点:

  • CFDictionaryCreateMutable 和 CFDictionarySetValue 不是线程安全的,所以需要创建锁,采用 dispatch_semaphore 通过控制信号量实现了锁

  • 设置缓存,如果在缓存中存在class,则直接获取到对应的ivar,method,property,否者创建YYClassInfo实例对象

NSObject+YYModel:

NSObject+YYModel是YYModel的核心类,主要部分:

强制内联C函数:功能函数
私有类_YYModelPropertyMeta : 管理Model属性的数据, 类型, 映射的key,keyPath
私有类 _YYModelMeta :管理Model 数据,类型,存储 映射key,keypath,_YYModelPropertyMeta
NSObject NSArray NSDictionary (YYModel) : 几个分类,YYModel主体功能实现
YYModel 协议:扩展功能实现

私有类_YYModelPropertyMeta

_YYModelPropertyMeta是对上面的 YYClassPropertyInfo 的进一步封装。

内部实例变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/// A property info in object model.
// model property的进一步分装
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; //属性名
YYEncodingType _type; //属性的编码类型
YYEncodingNSType _nsType; //属性的Foundation类型
BOOL _isCNumber; //是否c语言的数字
Class _cls; //属性的class
Class _genericCls; //属性内包含的类class
SEL _getter; //属性 getter方法
SEL _setter; //属性 setter方法
BOOL _isKVCCompatible; //是否可以使用KVC
BOOL _isStructAvailableForKeyedArchiver; //是否struct并且可以归档
BOOL _hasCustomClassFromDictionary; //是否包含本本地的class转换
/*
property->key:
_mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
property->keyPath:
_mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
property->keys:
_mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
*/
NSString *_mappedToKey; //属性名映射的 key
NSArray *_mappedToKeyPath; //属性名映射的 keyPath
NSArray *_mappedToKeyArray; //属性名的映射的key keyPath 数组
YYClassPropertyInfo *_info; //属性的YYClassPropertyInfo info
_YYModelPropertyMeta *_next;
//多个属性名映射到同一个key 时,指向下一个属性名的YYModelPropertyMeta 指针
}
@end

主要是最后几个变量。其中_mappedToKey _mappedToKeyPath _mappedToKeyArray 是属性映射的key,keyPath ,key (keypath) 数组_mappedToKeyArray 中可以是key和keyPath,实际取其中第一个映射到的值

一般一个属性名对应一个key值,如果多个属性名对应同一个key,这里就需要next发挥作用了。比如

1
2
3
4
5
{
@"name1" : @"name",
@"name2" : @"name",
@"name3" : @"name",
}

首先name1最先得到映射,对mapKey进行赋值,取得json中的name字段进行赋值一系列操作,此时next指针为nil
name2接着进行映射,对mapKey进行赋值,接着取得原来json key对应的属性描述对象,将name2的next指针,指向name1。
name3接着进行映射,对mapKey进行赋值,接着取得原来json key对应的属性描述对象,将name3的next指针,指向name2。

代码中的实现

1
2
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;

初始化方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
@implementation _YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
//创建并且根据propertyInfo 进行变量赋值
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name;
meta->_type = propertyInfo.type;
meta->_info = propertyInfo;
// 属性为容器类型的时候, 映射类型赋值
meta->_genericCls = generic;
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
// 是否Foundation 类型
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
} else {
// 是否C数据类型
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
}
// 属性为结构体类型
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
/*
It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
*/
static NSSet *types = nil;
static dispatch_once_t onceToken;
// 单例 创建C结构体类型映射
dispatch_once(&onceToken, ^{
NSMutableSet *set = [NSMutableSet new];
// 32 bit
[set addObject:@"{CGSize=ff}"];
[set addObject:@"{CGPoint=ff}"];
[set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
[set addObject:@"{CGAffineTransform=ffffff}"];
[set addObject:@"{UIEdgeInsets=ffff}"];
[set addObject:@"{UIOffset=ff}"];
// 64 bit
[set addObject:@"{CGSize=dd}"];
[set addObject:@"{CGPoint=dd}"];
[set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
[set addObject:@"{CGAffineTransform=dddddd}"];
[set addObject:@"{UIEdgeInsets=dddd}"];
[set addObject:@"{UIOffset=dd}"];
types = set;
});
// 只有上面结构体才能被归档
if ([types containsObject:propertyInfo.typeEncoding]) {
meta->_isStructAvailableForKeyedArchiver = YES;
}
}
// 设置class类型
meta->_cls = propertyInfo.cls;
// 如果是容器类型
if (generic) {
// 从容器class 中读取
meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
} else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
// 从class类型中读取
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
}
// 设置 getter 和 setter 方法
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
}
/**
* 只有实现了getter和setter方法 才能实现归档
*/
if (meta->_getter && meta->_setter) {
/*
类型是否支持 KVC
*/
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock:
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
meta->_isKVCCompatible = YES;
} break;
default: break;
}
}
return meta;
}
@end

私有类_YYModelMeta

_YYModelMeta 是对 YYClassInfo 的再次封装

内部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
// key: 映射的 json key ,keyPath value: _YYModelPropertyMeta
NSDictionary *_mapper;
// model所有属性的PropertyMetas
NSArray *_allPropertyMetas;
//model所有映射son keyPath 属性 的PropertyMetas
NSArray *_keyPathPropertyMetas;
// model所有映射多个key 属性 的PropertyMetas
NSArray *_multiKeysPropertyMetas;
/// 需要映射的属性总个数
NSUInteger _keyMappedCount;
/// Model对应的Foundation 类型
YYEncodingNSType _nsType;
// 事否实现了自定义的映射关系表
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
@end

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) {
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}

首先从缓存中加载,没有在根据传入cls 创建meta,并做缓存处理, dispatch_semaphore 确保线程安全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
@implementation _YYModelMeta
- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// 黑名单 会忽略返回数组里的属性
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// 白名单 只考虑返回数组内的属性
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// 获取容器属性中的映射关系字典
NSDictionary *genericMapper = nil;
// 判断类中是否实现了对应的modelContainerPropertyGenericClass方法
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
// 存储key和对应的class到字典中
if (genericMapper) {
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj);
if (!meta) return;
if (class_isMetaClass(meta)) {
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) {
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp;
}
}
// 存储所有属性的PropertyMeta对象
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
// 遍历当前ClassInfo 中的所有PropertyInfo, 将它们封装成PropertyMeta
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
// 检查是否合法和黑白名单筛选
if (!propertyInfo.name) continue;
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
// 通过propetyInfo来创建PropertyMeta 对象
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
// meta name非空
if (!meta || !meta->_name) continue;
// 需要实现get方法和set方法
if (!meta->_getter || !meta->_setter) continue;
// 字典中已有该字段的meta 避免重复操作
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta;
}
// 遍历父类的property
curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count)
_allPropertyMetas = allPropertyMetas.allValues.copy;
// 创建 key :propertyMeta 映射关系字典
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
// 是否实现自定义的映射表
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
// 遍历自定义的字典
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
// 由于用户自定义映射,把原来映射的数据删除
[allPropertyMetas removeObjectForKey:propertyName];
if ([mappedToKey isKindOfClass:[NSString class]]) {
// key字段非空
if (mappedToKey.length == 0) return;
// 保存映射的key
propertyMeta->_mappedToKey = mappedToKey;
// 如果是keyPath的情况处理
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
if (keyPath.count > 1) {
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
// 多个属性映射同一个key的时候,用next存储前一个 json Key映射的meta
propertyMeta->_next = mapper[mappedToKey] ?: nil;
// 保存新的meta对象
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
// 一个属性映射多个json Key
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
for (NSString *oneKey in ((NSArray *)mappedToKey)) {
if (![oneKey isKindOfClass:[NSString class]]) continue;
if (oneKey.length == 0) continue;
NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
if (keyPath.count > 1) {
[mappedToKeyArray addObject:keyPath];
} else {
[mappedToKeyArray addObject:oneKey];
}
if (!propertyMeta->_mappedToKey) {
propertyMeta->_mappedToKey = oneKey;
propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
}
}
if (!propertyMeta->_mappedToKey) return;
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
// 没有自定义映射规则的属性处理
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
// 直接让mappedKey等于属性名
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
// 变量存储
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
_classInfo = classInfo;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}

JSON 转 Model

1
2
3
4
+ (instancetype)yy_modelWithJSON:(id)son {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self yy_modelWithDictionary:dic];
}

传入的json可以是 NSDictionary, NSString , NSData_yy_dictionaryWithJSON 统一转化成字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
//字典合法性校验
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
//创建 model 元数据
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
//自定义字典
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
// 根据dictionary 进行 model 属性赋值
NSObject *one = [cls new];
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
}

结构体 ModelSetContext 存储 modelMeta ,model, dic 作为 CFDictionaryApplyFunctionCFArrayApplyFunction 的context 参数,传递数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
// 合法性检验
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
//映射的key Count >= dic Count
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
//映射的key Count < dic Count
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}

调用CoreFoundation 的 CFDictionaryApplyFunction 和 CFArrayApplyFunction 回调自定义的 Apply function
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context)static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context)

根据获得的value ,model ,_propertyMeta 最后统一调用 下面方法,运用runtime的 objc_msgSend 设置model属性

1
2
3
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta)

自定义的CFDictionaryApplyFunction 的回调方法,CoreFoundation中的原回调函数
typedef void (*CFDictionaryApplierFunction)(const void *key, const void *value, void *context);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
Apply function for dictionary, to set the key-value pair to model.
@param _key should not be nil, NSString.
@param _value should not be nil.
@param _context _context.modelMeta and _context.model should not be nil.
*/
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
__unsafe_unretained id model = (__bridge id)(context->model);
while (propertyMeta) {
if (propertyMeta->_setter) {
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}

自定义的CFArrayApplyFunction 的回调方法,CoreFoundation中的原回调函数
typedef void (*CFArrayApplierFunction)(const void *value, void *context);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
Apply function for model property meta, to set dictionary to model.
@param _propertyMeta should not be nil, _YYModelPropertyMeta.
@param _context _context.model and _context.dictionary should not be nil.
*/
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
if (!propertyMeta->_setter) return;
id value = nil;
if (propertyMeta->_mappedToKeyArray) {
//从dic中获取映射多个key的值,返回映射到的第一个值
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
//从dic中获取映射keypath的值
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
//从dic中获取映射key的值
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
if (value) {
//Model 属性赋值
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}

最终实现model 属性赋值。 该方法比较长,首先对 meta的属性类型进行判断,主要分为三类,

  • C基本数据类型
  • Foundation 类型
  • 其他类型,如 id, Class ,block,SEL等等
    根据类型获取对应value,

最后都调用 ((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value) 进行model 属性赋值

1
2
3
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta)

Model 转 JSON

其中有效的JSON Object 只能是以下类型:
NSArray,NSDictionary,NSString,NSNumber,NSNull。

1.如果是NSDictionary,NSSet,NSArray 类型,递归调用此方法获得JSON Object
2.如果是NSURL,NSAttributedString ,NSDate, NSData,做简单相应返回
3.根据Model类型创建modelMeta,取实例变量_mapper 获取所有属性名和`propertyMeta,再根据propertyMeta的 类型_type 获得相应的 value, 根据有无_mappedToKeyPath再进一步处理,赋值,最后 判断有无自定义_hasCustomTransformToDictionary,返回最终转化结果

主要方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
static id ModelToJSONObjectRecursive(NSObject *model) {
if (!model || model == (id)kCFNull) return model;
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) return model;
if ([model isKindOfClass:[NSDictionary class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableDictionary *newDic = [NSMutableDictionary new];
[((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
if (!stringKey) return;
id jsonObj = ModelToJSONObjectRecursive(obj);
if (!jsonObj) jsonObj = (id)kCFNull;
newDic[stringKey] = jsonObj;
}];
return newDic;
}
if ([model isKindOfClass:[NSSet class]]) {
NSArray *array = ((NSSet *)model).allObjects;
if ([NSJSONSerialization isValidJSONObject:array]) return array;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in array) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
if ([model isKindOfClass:[NSArray class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in (NSArray *)model) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
if ([model isKindOfClass:[NSData class]]) return nil;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
__unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
if (!propertyMeta->_getter) return;
id value = nil;
if (propertyMeta->_isCNumber) {
value = ModelCreateNumberFromProperty(model, propertyMeta);
} else if (propertyMeta->_nsType) {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
if (value == (id)kCFNull) value = nil;
} break;
case YYEncodingTypeClass: {
Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromClass(v) : nil;
} break;
case YYEncodingTypeSEL: {
SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromSelector(v) : nil;
} break;
default: break;
}
}
if (!value) return;
if (propertyMeta->_mappedToKeyPath) {
NSMutableDictionary *superDic = dic;
NSMutableDictionary *subDic = nil;
for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
NSString *key = propertyMeta->_mappedToKeyPath[i];
if (i + 1 == max) { // end
if (!superDic[key]) superDic[key] = value;
break;
}
subDic = superDic[key];
if (subDic) {
if ([subDic isKindOfClass:[NSDictionary class]]) {
subDic = subDic.mutableCopy;
superDic[key] = subDic;
} else {
break;
}
} else {
subDic = [NSMutableDictionary new];
superDic[key] = subDic;
}
superDic = subDic;
subDic = nil;
}
} else {
if (!dic[propertyMeta->_mappedToKey]) {
dic[propertyMeta->_mappedToKey] = value;
}
}
}];
if (modelMeta->_hasCustomTransformToDictionary) {
BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
if (!suc) return nil;
}
return result;
}

参考资料

郑钦洪_:YYModel 源码历险记

Contents
  1. 1. 前言
  2. 2. YYClassInfo
    1. 2.1. 要点
    2. 2.2. 变量类型编码
    3. 2.3. YYEncodingType
    4. 2.4. Var Method Property
    5. 2.5. 元类
    6. 2.6. 初始化方法
  3. 3. NSObject+YYModel:
    1. 3.1. 私有类_YYModelPropertyMeta
      1. 3.1.1. 内部实例变量
      2. 3.1.2. 初始化方法
    2. 3.2. 私有类_YYModelMeta
      1. 3.2.1. 内部变量
      2. 3.2.2. 初始化
    3. 3.3. JSON 转 Model
    4. 3.4. Model 转 JSON
  4. 4. 参考资料
|