JSONModel的源码解析
一、JSONModel和YYModel的比较
1、json中有[NSNull null]类型 — JSONModel和YYModel都有适配
2、异常情况:NSString <-> NSNumber — JSONModel和YYModel都有适配
3、异常情况:NSString <-> NSUInteger —JSONModel没有适配,会crash,YYModel有适配
4、异常情况:NSArray <-> NSString —JSONModel和YYModel都有没有适配
5、NSCoding 协议(持久化)的支持 —JSONModel和YYModel都有适配
6、是否可以嵌套Model —JSONModel和YYModel都有适配
7、NSArray中可以包含Model —JSONModel和YYModel都有适配
8、未知字段的适配(向后兼容) —JSONModel和YYModel都有适配
9、继承情况下对于多态的支持 —JSONModel没有适配,YYModel适配了
注意:好像YYModel再gethub上面的更新时间停留在2年前,好像近期都没有更新了。
二、JSONModel的源码解析
我们可以先看看核心代码
1、先是判断传入的字典是否为空,如果为空返回为空的错误
2、再判断传入的数据是否是字典类型,如果不是字典类型不正确的错误
3、核心的代码,通过init方法初始化映射property,(核心代码,等会再下面解释)
4、获取当前类的keyMapper
5、检查映射结构是否能从我们传入的dict中找到对应的数据,如果不能找到,就返回nil,并且抛出错误
6、根据传入的dict进行数据的赋值,如果赋值没有成功,就返回nil,并且抛出错误
7、根据本地的错误来判断是否有错误,如果有错误,就返回nil
8、返回self
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
//check for nil input
// 第一步: 先是判断传入的字典是否为空,如果为空返回为空的错误
if (!dict) {
if (err) *err = [JSONModelError errorInputIsNil];
return nil;
}
//invalid input, just create empty instance
//第二步:再判断传入的数据是否是字典类型,如果不是字典类型不正确的错误
if (![dict isKindOfClass:[NSDictionary class]]) {
if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
return nil;
}
//create a class instance
//第三步:核心的代码,通过init方法初始化映射property
self = [self init];
if (!self) {
//super init didn't succeed
if (err) *err = [JSONModelError errorModelIsInvalid];
return nil;
}
//第四步:获取当前类的keyMapper
//key mapping
JSONKeyMapper* keyMapper = [self __keyMapper];
//第五步:检查映射结构是否能从我们传入的dict中找到对应的数据,如果不能找到,就返回nil,并且抛出错误
//check incoming data structure
if (![self __doesDictionary:dict matchModelWithKeyMapper:keyMapper error:err]) {
return nil;
}
//import the data from a dictionary
//第六步:根据传入的dict进行数据的赋值,如果赋值没有成功,就返回nil,并且抛出错误。
if (![self __importDictionary:dict withKeyMapper:keyMapper validation:YES error:err]) {
return nil;
}
//第七步:根据本地的错误来判断是否有错误,如果有错误,就返回nil,并且抛出错误。
//run any custom model validation
if (![self validate:err]) {
return nil;
}
//第八步:返回self
//model is valid! yay!
return self;
}
第三步:核心代码的解析–通过init方法初始化映射property。
a、他会先调用init方法,init方法中点用了“setup“方法。–[self setup]。
-(id)init
{
self = [super init];
if (self) {
//do initial class setup
[self __setup__];
}
return self;
}
我们先解析一下“setup”方法
1、先是通过AssociateObject来判断是否进行过映射property的缓存,如果没有就使用“__inspectProperties”方法进行映射property的缓存–(后面我会解析一下“__inspectProperties”方法,介绍怎么进行映射property的缓存)
2、获取当前类的keyMapper映射。
3、判断一下,当前的keyMapper是否存在和是否进行映射过,如果没有进行映射就使用AssociateObject方法进行映射。
4、进行AssociateObject映射。
-(void)__setup__
{
//if first instance of this model, generate the property list
//第一步: 先是通过AssociateObject来判断是否进行过映射property的缓存,如果没有就使用“__inspectProperties”方法进行映射property的缓存
if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
//进行映射property的缓存
[self __inspectProperties];
}
//if there's a custom key mapper, store it in the associated object
//第二步:获取keyMapper
id mapper = [[self class] keyMapper];
//第三步:判断一下,当前的keyMapper是否存在和是否进行映射过,如果没有进行映射就使用AssociateObject方法进行映射
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
//第四步:进行AssociateObject映射
objc_setAssociatedObject(
self.class,
&kMapperObjectKey,
mapper,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
}
解析一下“__inspectProperties”方法,介绍怎么进行映射property的缓存
注意:NSScanner是用于在字符串中扫描指定的字符,特别是把它们翻译/转换为数字和别的字符串。可以在创建NSScaner时指定它的string属性,然后scanner会按照你的要求从头到尾地扫描这个字符串的每个字符。
1、先是获取当前class的property列表和个数
2、然后再遍历这些property
3、把我们的property通过一个局部变量进行赋值–JSONModelClassProperty,这个是JSONModel提供的类,来解析每个property。
4、获取property的名称给当前这个局部变量
5、获取这个property的属性
6、判断这个property的属性值里面是否包含”Tc,”,如果包含就设置structName为BOOL
7、扫描property属性
8、设置property的类型
9、判断并设置property的是否是可变的
10、判断property的是否我们允许的json类型
11、解析protocol的string
12、检查property是否为structure
13、判断property是不是Optional
14、判断property是不是Ignored
15、判断property是不是只读属性
16、通过kvc去设置相应的值
17、使用AssociateObject进行缓存
-(void)__inspectProperties
{
//JMLog(@"Inspect class: %@", [self class]);
NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];
//temp variables for the loops
Class class = [self class];
NSScanner* scanner = nil;
NSString* propertyType = nil;
// inspect inherited properties up to the JSONModel class
while (class != [JSONModel class]) {
//JMLog(@"inspecting: %@", NSStringFromClass(class));
//第一步:先是获取当前class的property列表和个数
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
//第二步:遍历property
//loop over the class properties
for (unsigned int i = 0; i < propertyCount; i++) {
//第三步:创建一个解析和判断每个property的局部变量JSONModelClassProperty
JSONModelClassProperty