序文
上一篇iOS读YYModel源码我们窥探了Json转Model的全过程,不过还差一步_YYModelMeta
实例化的过程,所以此文专门针对性的解读_YYModelMeta
实例化过程
解读
1 | _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls]; |
上层NSObject的Category YYModel仅仅调用了一下api,接下来我们将这一行进行展开,详细看看里边到底做了什么。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22+ (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;
}
这一段代码首先单例化了一个Dictionary缓存,并且先取缓存中的_YYModelMeta
,如果没有的话就实例化_YYModelMeta
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- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// Get black list
modelPropertyBlacklist
// Get white list
modelPropertyWhitelist
// Get container property's generic class
modelContainerPropertyGenericClass
// Create all property metas.
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
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;
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue;
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta;
}
curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
// create mapper
return self;
}
这个实例化代码有点长,我删减了一下。其实主要算是做了4件事情
- 实例化了
YYClassInfo
- 回调
YYModel
协议 - 根据上一步协议的回调结果blacklist、whitelist和集合泛型创建
_YYModelPropertyMeta
- 创建自定义的key与
_YYModelPropertyMeta
的映射
首先我来来看一下YYClassInfo的实例化,这个过程挺复杂的,也是核心部分
实例化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+ (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;
}
这个实例化与_YYModelMeta
的实例化方法是一样的,接下来我们重点关注[[YYClassInfo alloc] initWithClass:cls];
这个方法中调用了一个核心方法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- (void)_update {
_ivarInfos = nil;
_methodInfos = nil;
_propertyInfos = nil;
Class cls = self.cls;
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(cls, &methodCount);
if (methods) {
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
_methodInfos = methodInfos;
for (unsigned int i = 0; i < methodCount; i++) {
YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
if (info.name) methodInfos[info.name] = info;
}
free(methods);
}
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
_propertyInfos = propertyInfos;
for (unsigned int i = 0; i < propertyCount; i++) {
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
if (info.name) propertyInfos[info.name] = info;
}
free(properties);
}
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
if (ivars) {
NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
_ivarInfos = ivarInfos;
for (unsigned int i = 0; i < ivarCount; i++) {
YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
if (info.name) ivarInfos[info.name] = info;
}
free(ivars);
}
if (!_ivarInfos) _ivarInfos = @{};
if (!_methodInfos) _methodInfos = @{};
if (!_propertyInfos) _propertyInfos = @{};
_needUpdate = NO;
}
这个方法是通过runtime实现了对Model中属性、变量、方法提取,然后分别实例化了YYClassPropertyInfo
、YYClassIvarInfo
、YYClassMethodInfo
这三个类,并且存储到了相应的数组之中
根据上一步协议的回调结果blacklist、whitelist和集合泛型创建_YYModelPropertyMeta
这个过程主要是以下核心代码1
2
3_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
这个代码内部其实就是根据YYClassPropertyInfo
生成一个业务类,包括key的名字、类型、泛型类、是否是数字、setter方法、getter方法等等,其实主要是在YYClassPropertyInfo
这个类的基础了增加了一些业务逻辑的数据
创建自定义的key与_YYModelPropertyMeta的映射
这个过程就是根据YYModel
协议中的modelCustomPropertyMapper
方法,将自定义的key的键值对的映射进行处理1
2
3
4
5if ([mappedToKey isKindOfClass:[NSString class]]) {
...
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
...
}
这个是两种情况,一种是字符串(两种情况一种是单个的,一种是带.
,称之为keyPath);一种是数组的情况(一个model属性对应多个不同的key)。这两种情况都有一个单向链表实现
通过以上对数据的处理_YYModelMeta
中的属性_mapper、_allPropertyMetas、_keyPathPropertyMetas、_multiKeysPropertyMetas、_keyMappedCount等属性则均相应的存储上了数据
总结
这一层数据层设计的也相当的巧妙,有专门存储model信息的类,有专门处理我们自定义的业务逻辑类。上下分了两层。