Fork me on GitHub

iOS读YYModel源码之_YYModelMeta实例过程

序文

上一篇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中属性、变量、方法提取,然后分别实例化了YYClassPropertyInfoYYClassIvarInfoYYClassMethodInfo这三个类,并且存储到了相应的数组之中
根据上一步协议的回调结果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
5
if ([mappedToKey isKindOfClass:[NSString class]]) {
...
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
...
}

这个是两种情况,一种是字符串(两种情况一种是单个的,一种是带.,称之为keyPath);一种是数组的情况(一个model属性对应多个不同的key)。这两种情况都有一个单向链表实现

通过以上对数据的处理_YYModelMeta中的属性_mapper、_allPropertyMetas、_keyPathPropertyMetas、_multiKeysPropertyMetas、_keyMappedCount等属性则均相应的存储上了数据

总结

这一层数据层设计的也相当的巧妙,有专门存储model信息的类,有专门处理我们自定义的业务逻辑类。上下分了两层。

0%