注:本文搬运地址:https://www.jianshu.com/p/62d4f86c7482
YYModel --- 高性能 iOS/OSX 模型转换框架。
https://github.com/ibireme/YYModel
参考:
学习mark
https://www.jianshu.com/p/62d4f86c7482
http://www.cnblogs.com/guohai-stronger/p/9647269.html
实现解析
重要的类
- _YYModelMeta —————— 完成Model属性、方法、实例变量的解析,并生成与数据源相对应的字典映射;
- YYClassInfo —————— 存储Model解析后的各种信息;
- YYClassMethodInfo —————— 存储方法的信息;
- YYClassIvarInfo —————— 存储实例变量的信息;
- YYClassPropertyInfo —————— 存储属性的信息;
- _YYModelPropertyMeta —————— 该类包含了属性的信息和设置属性时所需要的信息,有上述解析得到的信息和自定义的信息合成,用于第二步中的映射关系生成。
实现步骤
1、解析实体信息
自定义的Model类调用modelWithDictionary:
、modelWithJSON:
进行Model的初始化,接下来_YYModelMeta
类将会为我们完成解析的工作。解析的目的是获取Model类的方法、属性、实例变量信息,这些信息将保存在YYClassInfo
中。
在解析之初会首先检查是否存在缓存,如果有缓存则直接返回缓存的_YYModelMeta
对象。通常情况下,每一个类的属性、实例变量的解析只会进行一次,成功解析一次后的数据将会被缓存起来,只有当设置了YYClassInfo
的_needUpdate
才会进行新的解析,也就是进行动态的添加属性、修改方法后需要更新。缓存数据保存在一个静态的CFMutableDictionaryRef
字典当中,并通过dispatch_semaphore_t
信号量来确保字典读取的线程安全性。
没有缓存,则创建_YYModelMeta
和YYClassInfo
对象,其中YYClassInfo
也加入了缓存处理,个人觉得这里是没有必要的,因为_YYModelMeta
已经有属性引用了YYClassInfo
。
YYClassInfo
的_update
会为我们完成具体的解析,该方法依次对方法、属性、实例变量进行了解析,个人发现方法、实例变量解析的信息并未用到。所以这里着重说一下属性的解析,通过class_copyPropertyList
获取到属性列表,并为每一个属性生成一个YYClassPropertyInfo
对象,该对象会保存在YYClassInfo
的_propertyInfos
数组中。生成的YYClassPropertyInfo
对象主要包含了属性名、setter、getter、属性数据类型等,这些信息都有对应的runtime方法。其中核心在于利用类型编码完成属性数据类型的解析。
解析属性的核心代码:
/// 1.获取Model类的属性列表
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
// 将生成的YYClassPropertyInfo对象保存在该数组中
_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);
}
/// 2.初始化YYClassPropertyInfo对象
- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [super init];
_property = property;
// 获取属性的名称
const char *name = property_getName(property);
if (name) {
// 将属性名称有C字符串转化为NSString
_name = [NSString stringWithUTF8String:name];
}
YYEncodingType type = 0;
unsigned int attrCount;
// 获取属性对应的所有描述,即objc_property_attribute_t的结构体数组,包括原子性、数据类型、内存语义等
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
// objc_property_attribute_t的name为C字符串,name[0]表示获取第一个字符
switch (attrs[i].name[0]) {
case 'T': { // 这里将解析数据的类型,比如NSString、BOOL、NSArray等
if (attrs[i].value) {
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
// 刚方法用于解析具体的数据类型
type = YYEncodingGetType(attrs[i].value);
// 只有当数据类型为NSObject类,包括采纳了协议的属性,才需要如下进一步的解析
if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
// 将解析到的数据类型的类保存在_cls属性中
if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
}
// 对于采纳协议的属性的解析
NSMutableArray *protocols = nil;
while ([scanner scanString:@"<" intoString:NULL]) {
NSString* protocol = nil;
if ([scanner scanUpToString:@">" intoString: &protocol]) {
if (protocol.length) {
if (!protocols) protocols = [NSMutableArray new];
[protocols addObject:protocol];
}
}
[scanner scanString:@">" intoString:NULL];
}
_protocols = protocols;
}
}
} break;
case 'V': { // Instance variable
if (attrs[i].value) {
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
}
} break;
case 'R': {
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': {
type |= YYEncodingTypePropertyCopy;
} break;
case '&': {
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': {
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;
case 'W': {
type |= YYEncodingTypePropertyWeak;
} break;
case 'G': { // 获取自定义的getter方法
type |= YYEncodingTypePropertyCustomGetter;
if (attrs[i].value) {
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
case 'S': { // 获取自定义的setter方法
type |= YYEncodingTypePropertyCustomSetter;
if (attrs[i].value) {
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} // break; commented for code coverage in next line
default: break;
}
}
if (attrs) {
free(attrs);
attrs = NULL;
}
// 保存获取的type以及验证_setter、_getter是否已经获取到,这三个要素在设置属性值时将会被用到
_type = type;
if (_name.length) {
if (!_getter) {
_getter = NSSelectorFromString(_name);
}
if (!_setter) {
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
}
}
return self;
}
2、生成映射关系
通过生成的YYClassInfo来建立与数据源之间的映射关系,也就是生成了一个以数据源字典的key为key,以_YYModelPropertyMeta对象为值的字典_mapper,核心类为_YYModelPropertyMeta,该类包含了属性的信息和用户自定义的信息,例如属性的setter、getter、对应数据源的key、子类容器等。
在生成_mapper的过程中包括白名单、黑名单、容器类、自定义数据源的key与属性名映射关系的处理,具体可参见_YYModelMeta类的initWithClass:方法。
3、设置属性值
上述两个步骤均是在_YYModelMeta中完成的,第三部回到NSObject+YYModel分类中,用modelSetWithDictionary:方法完成属性值的设置。
核心代码:
/// 使用下面的函数,遍历字典并通过ModelSetWithDictionaryFunction函数完成Model属性值的设置
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
/// context为ModelSetContext结构体
typedef struct {
void *modelMeta; /// 为步骤1、2完成后生成的_YYModelMeta对象
void *model; /// 要生成的实体类,相当于id类型
void *dictionary; /// 数据源
} ModelSetContext;
/// 属性值的设置最终通过objc_msgSend函数实现,例如下列对NSString类型的属性进行设置
if ([value isKindOfClass:[NSString class]]) {
if (meta->_nsType == YYEncodingTypeNSString) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
}
}
网友评论