每个IOS开发人员对于JSONModel都是应该比较熟悉的,这次达内科技的金牌讲师就打算专门谈谈它,掌握这个JSONModel对于ios培训的同学们来说是举足轻重的。
JSONModel三)
JSONModel是一个解析服务器返回的Json数据的库。
通常服务器传回的json数据要通过写一个数据转换模块将NSDictionary转换为Model,将NSString数据转换为Model中property的数据类型。
这样服务器如果要做修改,可能需要改两三个文件。
JSONModel的出现就是为了将这种解析工作在设计层面完成。
对其源码的核心部分JSONModel.m做了源码阅读,笔记如下:
在
-(id)initWithDictionary:(NSDictionary*)dicterror:(NSError**)err
函数中完成所有解析工作:如果有任何失误或者错误直接返回nil。
-(id)initWithDictionary:(NSDictionary*)dicterror:(NSError**)err
{
//1、做有效性判断(dict是不是空啊,dict是不是真是一个NSDictionary)
//checkfornilinput
if(!dict){
if(err)*err=[JSONModelErrorerrorInputIsNil];
returnnil;
}
//invalidinput,justcreateemptyinstance
if(![dictisKindOfClass:[NSDictionaryclass]]){
if(err)*err=[JSONModelErrorerrorInvalidData];
returnnil;
}
//createaclassinstance
self=[superinit];
if(!self){
//superinitdidn'tsucceed
if(err)*err=[JSONModelErrorerrorModelIsInvalid];
returnnil;
}
//__setup__中通过调用__restrospectProperties建立类属性的映射表,并且存放在全局变量classProperties里面
//->__restrospectProperties中利用runtimefunction搞出属性列表:
//->获得属性列表class_copyPropertyList(得到objc_property_t数组)->对于每一个objc_property_t调用property_getName获得名称,property_getAttributes获得属性的描述(字符串)->通过解析字符串获得属性的类型、是否是Mutable、是否是基本的JSON类型等等
//->调用[classsuperclass]获得父类继续获取列表
//->列表保存在classProperties中备用
//->调用+keyMapper获得key转换列表,生成JSONKeyMapper对象存入keyMapper。
//doinitialclasssetup,retrospecproperties
[self__setup__];
//看看必传参数中是否在输入参数中都有。
//checkifallrequiredpropertiesarepresent
NSArray*incomingKeysArray=[dictallKeys];
NSMutableSet*requiredProperties=[self__requiredPropertyNames];
NSSet*incomingKeys=[NSSetsetWithArray:incomingKeysArray];
//getthekeymapper
JSONKeyMapper*keyMapper=keyMappers[__className_];
//transformthekeynames,ifneccessary
if(keyMapper){
//对比dict输入的keyName导入NSSet与keyMapper中JSONKeyMapper对象做keyName的转换。统一转换为对象的propertyname。
NSMutableSet*transformedIncomingKeys=[NSMutableSetsetWithCapacity:requiredProperties.count];
NSString*transformedName=nil;
//loopovertherequiredpropertieslist
for(NSString*requiredPropertyNameinrequiredProperties){
//getthemappedkeypath
transformedName=keyMapper.modelToJSONKeyBlock(requiredPropertyName);
//chekifexistsandifso,addtoincomingkeys
if([dictvalueForKeyPath:transformedName]){
[transformedIncomingKeysaddObject:requiredPropertyName];
}
}
//overwritetherawincominglistwiththemappedkeynames
incomingKeys=transformedIncomingKeys;
}
JSONModel(二)
//利用NSSet的isSubsetOfSet:将必传参数表与输入的keyName表对比。如果不是包含关系说明参数传的不够。 //checkformissinginputkeys
if(![requiredPropertiesisSubsetOfSet:incomingKeys]){
//getalistofthemissingproperties
[requiredPropertiesminusSet:incomingKeys];
//notallrequiredpropertiesarein-invalidinput
JMLog(@"Incomingdatawasinvalid[%@initWithDictionary:].Keysmissing:%@",self._className_,requiredProperties);
if(err)*err=[JSONModelErrorerrorInvalidDataWithMissingKeys:requiredProperties]; returnnil;
}
//notneededanymore
incomingKeys=nil;
requiredProperties=nil;
//从对象的classProperties列表中循环到dict中取值:(赋值使用KVO操作的setValue:forKey:来做的,这样会直接调用setter函数赋值)
//loopovertheincomingkeysandsetself'sproperties
for(JSONModelClassProperty*propertyin[self__properties__]){
//对于每一个对象的property,通过keyMapper的转换找到对应dictproperty的dictKeyPath,找到值jsonValue。如果没有值,并且这个属性是Optional的就进行下一项property对比。
//convertkeynameotmodelkeys,ifamapperisprovided
NSString*jsonKeyPath=property.name;
if(keyMapper)jsonKeyPath=keyMapper.modelToJSONKeyBlock(property.name);
//JMLog(@"keyPath:%@",jsonKeyPath);
//generalcheckfordatatypecompliance
idjsonValue=[dictvalueForKeyPath:jsonKeyPath];
//checkforOptionalproperties
if(jsonValue==nil&&property.isOptional==YES){
//skipthisproperty,continuewithnextproperty continue;
}
//对找到的值做类型判断,如果不是JSON应该返回的数据类型就报错。(注意:NSNull是可以作为参数回传的)
ClassjsonValueClass=[jsonValueclass];
BOOLisValueOfAllowedType=NO;
for(ClassallowedTypeinallowedJSONTypes){
if([jsonValueClassisSubclassOfClass:allowedType]){
isValueOfAllowedType=YES;
break;
}
}
if(isValueOfAllowedType==NO){
//typenotallowed
JMLog(@"Type%@isnotallowedinJSON.",NSStringFromClass(jsonValueClass));
if(err)*err=[JSONModelErrorerrorInvalidData];
returnnil;
}
//checkifthere'smatchingpropertyinthemodel
//JSONModelClassProperty*property=classProperties[self.className][key];
//接着对property的属性与jsonValue进行类型匹配:
if(property){
//如果是基本类型(int/float等)直接值拷贝;
//0)handleprimitives
if(property.type==nil&&property.structName==nil){
//justcopythevalue
[selfsetValue:jsonValueforKey:property.name];
//skipdirectlytothenextkey
continue;
}
//如果是NSNull直接赋空值;
//0.5)handlenils
if(isNull(jsonValue)){
[selfsetValue:nilforKey:property.name];
continue;
}
//如果是值也是一个JsonModel,递归搞JsonModel
//1)checkifpropertyisitselfaJSONModel
if([[property.typeclass]isSubclassOfClass:[JSONModelclass]]){
//initializetheproperty'smodel,storeit
NSError*initError=nil;
idvalue=[[property.typealloc]initWithDictionary:jsonValueerror:&initError];
if(!value){
if(initError&&err)*err=[JSONModelErrorerrorInvalidData];
returnnil;
}
[selfsetValue:valueforKey:property.name];
//forclarity,doesthesamewithoutcontinue
continue;
}else{
//如果property中有protocol解析将jsonValue按照protocol解析,如NSArray<JsonModelSubclass>,protocol就是JsonModelSubclass
//2)checkifthere'saprotocoltotheproperty
//)mightornotbethecasethere'sabuiltintransofrmforit
if(property.protocol){
//JMLog(@"proto:%@",p.protocol);
//__transform:forProperty:函数功能:
//->先判断下protocolClass是否在运行环境中存在,如不存在并且property是NSArray类型,直接报错。否则,直接返回。
//->如果protocalClass是JsonModel的子类,
//->如果property.type是NSArray
//->判断一下是否是使用时转换
//->如果为使用时转换则输出一个JSONModelArray(NSArray)的子类
//->如果不是使用时转换则输出一个NSArray,其中的对象全部转换为protocalClass所对应对象
//->如果property.type是NSDictionary
//->将value转换为protocalClass所对应对象
//->根据key存储到一个NSDictionary中输出
jsonValue=[self__transform:jsonValueforProperty:property];
if(!jsonValue){
if(err)*err=[JSONModelErrorerrorInvalidData];
returnnil;
}
//如果是基本JSON类型(NSString/NSNumber)
//3.1)handlematchingstandardJSONtypes
if(property.isStandardJSONType&&[jsonValueisKindOfClass:property.type]){
//如果是mutable的,做一份MutableCopy
//mutableproperties
if(property.isMutable){
jsonValue=[jsonValuemutableCopy];
}
//setthepropertyvalue
[selfsetValue:jsonValueforKey:property.name];
continue;
}
//如果property.type是NSArray
//3.3)handlevaluestotransform
if(
//如果(类型没有匹配,并且jsonValue不为空)或者是Mutable的property(说明是特殊类型转换)
(![jsonValueisKindOfClass:property.type]&&!isNull(jsonValue))
||
//thepropertyismutable
property.isMutable
){
//利用JSONValueTransformer找到源类型
//searchedaroundthewebhowtodothisbetter
//butdidnotfindanysolution,maybethat'sthebestidea?(hardly)
ClasssourceClass=[JSONValueTransformerclassByResolvingClusterClasses:[jsonValueclass]];
//JMLog(@"totype:[%@]fromtype:[%@]transformer:[%@]",p.type,sourceClass,selectorName);
//用字符串拼出转换函数的名称字符串,到JSONValueTransformer中去搜索@SEL执行出正确类型
//buildamethodselectorforthepropertyandjsonobjectclasses
NSString*selectorName=[NSStringstringWithFormat:@"%@From%@:",
(property.structName?property.structName:property.type),//targetname
sourceClass];//sourcename
SELselector=NSSelectorFromString(selectorName);
//checkifthere'satransformerwiththatname
if([valueTransformerrespondsToSelector:selector]){
//it'sOK,believeme...
#pragmaclangdiagnosticpush
#pragmaclangdiagnosticignored"-Warc-performSelector-leaks"
//transformthevalue
jsonValue=[valueTransformerperformSelector:selectorwithObject:jsonValue];
#pragmaclangdiagnosticpop
[selfsetValue:jsonValueforKey:property.name];
}else{
//it'snotaJSONdatatype,andthere'snotransformerforit
//ifpropertytypeisnotsupported-that'saprogrammermistaked->exception
@throw[NSExceptionexceptionWithName:@"Typenotallowed"
reason:[NSStringstringWithFormat:@"%@typenotsupportedfor%@.%@",property.type,[selfclass],property.name]
userInfo:nil];
returnnil;
}
}else{
//哪儿都不是的直接存起来
//3.4)handle"allother"cases(ifany)
[selfsetValue:jsonValueforKey:property.name];
}
}
}
}
//最后调用validate:看看结果是不是有效,没问题就返回了。
//runanycustommodelvalidation
NSError*validationError=nil;
BOOLdoesModelDataValidate=[selfvalidate:&validationError];
if(doesModelDataValidate==NO){
if(err)*err=validationError;
returnnil;
}
//modelisvalid!yay!
returnself;
}
程序亮点:
1、为了提高效率通过static的NSArray和NSDictionary进行解耦。
2、JSONValueTransformer实现了一个可复用的类型转换模板。
3、通过runtimefunction解析出property列表,通过property相关函数解析出名称,和Attributes的信息。
4、NSScanner的使用
5、NSSet的包含关系判断两个集合的交集
6、利用[NSObjectsetValue:forKey:]的KVO操作赋值,可以直接调用setter函数,并且可以赋nil到property中
7、适时给子类一个函数可以修改父类的一些行为
聪明在于勤奋,天才在于积累,对于学习语言来说,勤奋和积累是非常必要的,把一个技术点悟透,只有反复不断的练习,达内希望每一位同学都能把学习当成一种信仰,不断的充实自己完善自己。