邯郸网站建设优化排名,开封美食网站建设规划,台州路桥做网站的公司有哪些,网站维护什么情况JSONModel 文章目录JSONModel关于JSONModel的用法initWithDictionary等方法load方法实现load方法调用时机init方法__setup__方法__inspectProperties:方法__doesDictionary方法__importDictionary方法关于JSONModel的用法
可以参考之前写的博客#xff1a;【iOS】—— JSONMo…JSONModel 文章目录JSONModel关于JSONModel的用法initWithDictionary等方法load方法实现load方法调用时机init方法__setup__方法__inspectProperties:方法__doesDictionary方法__importDictionary方法关于JSONModel的用法
可以参考之前写的博客【iOS】—— JSONModel使用以及Manager封装网络请求
initWithDictionary等方法
整个过程用到的变量
#pragma mark - associated objects names //关联对象名称
static const char * kMapperObjectKey;
//关联对象kMapperObjectKey保存自定义的mapperstatic const char * kClassPropertiesKey;
//关联对象kClassPropertiesKey用来保存所有属性信息的NSDictionarystatic const char * kClassRequiredPropertyNamesKey;
//关联对象kClassRequiredPropertyNamesKey用来保存所有属性的名称NSSetstatic const char * kIndexPropertyNameKey;
//关联对象kIndexPropertyNameKey储存符合 Index 协议的属性名#pragma mark - class static variables //类静态变量
static NSArray* allowedJSONTypes nil;
static NSArray* allowedPrimitiveTypes nil;
static JSONValueTransformer* valueTransformer nil;
static Class JSONModelClass NULL; JSONModel中含有许多init方法
- (instancetype)initWithDictionary:(NSDictionary *)dict error:(NSError **)err;
- (instancetype)initWithData:(NSData *)data error:(NSError **)error;
- (instancetype)initWithString:(NSString *)string error:(JSONModelError **)err;
- (instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err;我们仔细观察即可发现前面的方法在实现过程中做了简单的处理最终还是调用了initWithDictionary
-(instancetype)initWithData:(NSData *)data error:(NSError *__autoreleasing *)err
{//check for nil inputif (!data) {if (err) *err [JSONModelError errorInputIsNil];return nil;}//read the jsonJSONModelError* initError nil;id obj [NSJSONSerialization JSONObjectWithData:dataoptions:kNilOptionserror:initError];if (initError) {if (err) *err [JSONModelError errorBadJSON];return nil;}//init with dictionaryid objModel [self initWithDictionary:obj error:initError];if (initError err) *err initError;return objModel;
}-(id)initWithString:(NSString*)string error:(JSONModelError**)err
{JSONModelError* initError nil;id objModel [self initWithString:string usingEncoding:NSUTF8StringEncoding error:initError];if (initError err) *err initError;return objModel;
}-(id)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err
{//check for nil inputif (!string) {if (err) *err [JSONModelError errorInputIsNil];return nil;}JSONModelError* initError nil;id objModel [self initWithData:[string dataUsingEncoding:encoding] error:initError];if (initError err) *err initError;return objModel;}那我们来看看initWithDictionary方法的实现
//这个方法里包含了作者做到的所有的容错和模型转化
//几个重要的点
//关联对象kClassPropertiesKey:(用来保存所有属性信息的NSDictionary)
//关联对象kClassRequiredPropertyNamesKey:(用来保存所有属性的名称的NSSet)
//关联对象kMapperObjectKey:(用来保存JSONKeyMapper):自定义的mapper具体的方法就是用来自定义修改接受数据中的key
//JSONModelClassProperty:封装的jsonmodel的一个属性它包含了对应属性的名字例如 name:gender,类型例如 type:NSString,是否是JSONModel支持的类型isStandardJSONType:YES/NO,是否是可变对象(isMutable:YES/NO)等属性。
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{//check for nil input//1.第一步判断传入的是否为nilif (!dict) {if (err) *err [JSONModelError errorInputIsNil];return nil;}//invalid input, just create empty instance//2.第二步判断传入的是否为字典类型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//3.创建类实例通过init方法初始化映射propertyself [self init];if (!self) {//super init didnt succeedif (err) *err [JSONModelError errorModelIsInvalid];return nil;}//check incoming data structure//4.检查映射结构是否能从我们传入的dict中找到对应的数据如果不能找到就返回nil并且抛出错误if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {return nil;}//import the data from a dictionary//5.根据传入的dict进行数据的赋值如果赋值没有成功就返回nil并且抛出错误。if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {return nil;}//run any custom model validation//6.根据本地的错误来判断是否有错误如果有错误就返回nil并且抛出错误。if (![self validate:err]) {return nil;}//model is valid! yay!//7.前面的判断都通过返回selfreturn self;
}方法1-4:都是对错误的发现与处理。方法5:是真正的mapping。方法6:是作者给用户自己定义错误的方法如果复合了用户自己定义的错误那么即使mapping成功了也要返回nil。方法7:成功返回模型对象。
整个代码的执行流程 首先在这个模型类的对象被初始化的时候遍历自身到所有的父类直到JSONModel为止获取所有的属性并将其保存在一个字典里。获取传入字典的所有key将这些key与保存的所有属性进行匹配。如果匹配成功则进行kvc赋值。
load方法实现
load方法调用时机
这里我们不得不提一嘴load方法的调用时机 load方法会在加载类的时候就被调用也就是iOS应用启动的时候就会加载所有的类就会调用每个类的load方法。load 方法会被默认执行并且是在 main 函数之前执行的。并没有对类做出任何操作的情况下调用。 load 方法当类被加载时它会自动被调用。这个调用非常早。如果你实现了一个应用或框架的 load并且你的应用链接到这个框架上了那么 load 会在 main() 函数之前被调用。如果你在一个可加载的 bundle 中实现了 load那么它会在 bundle 加载的过程中被调用。
(void)load
{static dispatch_once_t once;dispatch_once(once, ^{// initialize all class static objects,// which are common for ALL JSONModel subclassesautoreleasepool {//兼容的对象属性allowedJSONTypes [[NSString class], [NSNumber class], [NSDecimalNumber class], [NSArray class], [NSDictionary class], [NSNull class], //immutable JSON classes[NSMutableString class], [NSMutableArray class], [NSMutableDictionary class] //mutable JSON classes];//兼容的基本类型属性allowedPrimitiveTypes [BOOL, float, int, long, double, short,unsigned int, usigned long, long long, unsigned long long, unsigned short, char, unsigned char,//and some famous aliasesNSInteger, NSUInteger,Block];//valueTransformer是值转换器的意思valueTransformer [[JSONValueTransformer alloc] init];// This is quite strange, but I found the test isSubclassOfClass: (line ~291) to fail if using [JSONModel class].// somewhat related: https://stackoverflow.com/questions/6524165/nsclassfromstring-vs-classnamednsstring// //; seems to break the unit tests// Using NSClassFromString instead of [JSONModel class], as this was breaking unit tests, see below//http://stackoverflow.com/questions/21394919/xcode-5-unit-test-seeing-wrong-class//总结在对JSONModel类进行判断isSubclassOfClass时后面写的标准类最好用NSClassFromString而不是[JSONModel class]JSONModelClass NSClassFromString(NSStringFromClass(self));}});
}init方法
__setup__方法
在initWithDictionary方法中第三步调用了init方法我们来看看init方法的实现
-(void)__setup__
{//if first instance of this model, generate the property list//1.通过objc_getAssociatedObject来判断是否进行过映射property的缓存//如果没有就使用“__inspectProperties”方法进行映射property的缓存//在这里补充下objc_getAssociatedObject作用获取相关联的对象通过创建的时候设置的key来获取if (!objc_getAssociatedObject(self.class, kClassPropertiesKey)) {[self __inspectProperties];}//if theres a custom key mapper, store it in the associated object//2.判断一下当前的keyMapper是否存在和是否进行映射过如果没有进行映射就使用AssociateObject方法进行映射id mapper [[self class] keyMapper];if ( mapper !objc_getAssociatedObject(self.class, kMapperObjectKey) ) {//3.进行AssociateObject映射objc_setAssociatedObject(self.class,kMapperObjectKey,mapper,OBJC_ASSOCIATION_RETAIN // This is atomic);}
}-(id)init
{self [super init];if (self) {//do initial class setup[self __setup__];}return self;
}__inspectProperties:方法
key mapper主要是用来针对某些json字段名和model数据名不一致的情况。__setup__方法中调用了一个__inspectProperties:方法这个方法是这个框架的核心方法之一它的任务是保存了所有需要赋值的属性用作将来与传进来的字典进行映射。具体代码实现如下
//inspects the class, gets a list of the class properties
//__setup__中调用这个方法检查类得到类属性的列表它的任务是保存所有需要赋值的属性
-(void)__inspectProperties
{//JMLog(Inspect class: %, [self class]);// 最终保存所有属性的字典形式为例子// {// age property primitive age (Setters []);// friends property NSArray* friends (Standard JSON type, Setters []);// gender property NSString* gender (Standard JSON type, Setters []);// name property NSString* name (Standard JSON type, Setters []);// }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//检查继承的属性直到JSONModel类循环条件当class是JSONModel自己的时候不执行while (class ! [JSONModel class]) {//JMLog(inspecting: %, NSStringFromClass(class));//属性的个数unsigned int propertyCount;//获得属性列表所有property声明的属性objc_property_t *properties class_copyPropertyList(class, propertyCount);//loop over the class properties//循环遍历所有的属性for (unsigned int i 0; i propertyCount; i) {//JSONModel里的每一个属性都被封装成一个JSONModelClassProperty对象JSONModelClassProperty* p [[JSONModelClassProperty alloc] init];//get property name//获取属性名objc_property_t property properties[i]; //获取当前属性const char *propertyName property_getName(property); //namec字符串p.name (propertyName); //propertyNeme:属性名称例如nameagegender//JMLog(property: %, p.name);//get property attributes//获取属性属性const char *attrs property_getAttributes(property);NSString* propertyAttributes (attrs);NSArray* attributeItems [propertyAttributes componentsSeparatedByString:,];//ignore read-only properties//说明是只读属性不做任何操作if ([attributeItems containsObject:R]) {continue; //to next property到下一个属性}// 实例化一个scanner扫描仪用于查找相关字符串等功能scanner [NSScanner scannerWithString: propertyAttributes];//JMLog(attr: %, [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);[scanner scanUpToString:T intoString: nil];[scanner scanString:T intoString:nil];//check if the property is an instance of a class//检查属性是否是类的实例if ([scanner scanString:\ intoString: propertyType]) {//属性是一个对象[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:\]intoString:propertyType];//JMLog(type: %, propertyClassName);//设置property的类型p.type NSClassFromString(propertyType);// 判断并设置property的是否是可变的p.isMutable ([propertyType rangeOfString:Mutable].location ! NSNotFound);// 判断property的是否我们允许的json类型p.isStandardJSONType [allowedJSONTypes containsObject:p.type];//read through the property protocols//通读属性协议解析protocol的stringwhile ([scanner scanString: intoString:NULL]) {NSString* protocolName nil;[scanner scanUpToString: intoString: protocolName];if ([protocolName isEqualToString:Optional]) {p.isOptional YES;} else if([protocolName isEqualToString:Index]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored -Wdeprecated-declarationsp.isIndex YES;
#pragma GCC diagnostic popobjc_setAssociatedObject(self.class,kIndexPropertyNameKey,p.name,OBJC_ASSOCIATION_RETAIN // This is atomic);} else if([protocolName isEqualToString:Ignore]) {p nil;} else {p.protocol protocolName;}//到最接近的为止[scanner scanString: intoString:NULL];}}//check if the property is a structure检查属性是否为structureelse if ([scanner scanString:{ intoString: propertyType]) {//属性是结构体[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]intoString:propertyType];p.isStandardJSONType NO;p.structName propertyType;}//the property must be a primitive属性必须是基本类型比如int float等else {//the property contains a primitive data type//该属性包含基元数据类型[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:,]intoString:propertyType];//get the full name of the primitive type//获取基元类型的全名propertyType valueTransformer.primitivesNames[propertyType];if (![allowedPrimitiveTypes containsObject:propertyType]) {//type not allowed - programmer mistaken - exception//类型不允许 - 程序员错误 - 异常throw [NSException exceptionWithName:JSONModelProperty type not allowedreason:[NSString stringWithFormat:Property type of %.% is not supported by JSONModel., self.class, p.name]userInfo:nil];}}NSString *nsPropertyName (propertyName);// 判断property是不是Optionalif([[self class] propertyIsOptional:nsPropertyName]){p.isOptional YES;}// 判断property是不是Ignoredif([[self class] propertyIsIgnored:nsPropertyName]){p nil;}//集合类Class customClass [[self class] classForCollectionProperty:nsPropertyName];if (customClass) {p.protocol NSStringFromClass(customClass);}//few cases where JSONModel will ignore properties automatically//很少有JSONModel会自动忽略属性的情况忽略blockif ([propertyType isEqualToString:Block]) {p nil;}//add the property object to the temp index//将属性对象添加到临时索引如果字典里不存在则添加到属性字典里if (p ![propertyIndex objectForKey:p.name]) {[propertyIndex setValue:p forKey:p.name];}// generate custom setters and getter//生成自定义setter和getterif (p){//name - Name(大写p的名字属性的前两个字母)NSString *name [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];// getterSEL getter NSSelectorFromString([NSString stringWithFormat:JSONObjectFor%, name]);if ([self respondsToSelector:getter])p.customGetter getter;// settersp.customSetters [NSMutableDictionary new];SEL genericSetter NSSelectorFromString([NSString stringWithFormat:set%WithJSONObject:, name]);if ([self respondsToSelector:genericSetter])p.customSetters[generic] [NSValue valueWithBytes:genericSetter objCType:encode(SEL)];for (Class type in allowedJSONTypes){NSString *class NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:type]);if (p.customSetters[class])continue;SEL setter NSSelectorFromString([NSString stringWithFormat:set%With%:, name, class]);if ([self respondsToSelector:setter])p.customSetters[class] [NSValue valueWithBytes:setter objCType:encode(SEL)];}}}//释放属性列表free(properties);//ascend to the super of the class//(will do that until it reaches the root class - JSONModel)//升到class上的最高级//将这样做直到它到达根类-JSONModel再指向自己的父类直到等于JSONModel才停止class [class superclass];}//finally store the property index in the static property index//最后将属性索引属性列表存储在静态属性索引中//最后保存所有当前类JSONModel的所有的父类的属性objc_setAssociatedObject(self.class,kClassPropertiesKey,[propertyIndex copy],OBJC_ASSOCIATION_RETAIN // This is atomic);
}这个方法的大概过程 1.先是获取当前class的property列表和个数 2.然后再遍历这些property 3.把我们的property通过一个局部变量进行赋值–JSONModelClassProperty这个是JSONModel提供的类来解析每个property。 4.获取property的名称给当前这个局部变量 5.获取这个property的属性 6.扫描property属性 7.设置property的类型 8.判断并设置property的是否是可变的 9.判断property的是否我们允许的json类型 10.解析protocol的string 11.检查property是否为structure 12.判断property是不是Optional 13.判断property是不是Ignored 14.判断property是不是只读属性 15.通过kvc去设置相应的值 16.使用AssociateObject进行缓存
接下来我们来看initWithDictionary的第四步
__doesDictionary方法
//model类里面定义的属性集合是不能大于传入的字典里的key集合的。
//如果存在了用户自定义的mapper则需要按照用户的定义来进行转换。
//例如将gender转换为了sex。
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{//check if all required properties are present//1.检查一下所有的必要属性都存在并且把他们都放入set中NSArray* incomingKeysArray [dict allKeys];NSMutableSet* requiredProperties [self __requiredPropertyNames].mutableCopy;//从array拿到setNSSet* incomingKeys [NSSet setWithArray: incomingKeysArray];//transform the key names, if necessary//如有必要变换键名称;如果用户自定义了mapper则进行转换if (keyMapper || globalKeyMapper) {NSMutableSet* transformedIncomingKeys [NSMutableSet setWithCapacity: requiredProperties.count];NSString* transformedName nil;//loop over the required properties list//在所需属性列表上循环,遍历需要转换的属性列表for (JSONModelClassProperty* property in [self __properties__]) {//被转换成的属性名称例如gender模型内 - sex字典内transformedName (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;//check if exists and if so, add to incoming keys//检查是否存在如果存在则添加到传入密钥//例如拿到sex以后查看传入的字典里是否有sex对应的值id value;try {value [dict valueForKeyPath:transformedName];}catch (NSException *exception) {value dict[transformedName];}if (value) {[transformedIncomingKeys addObject: property.name];}}//overwrite the raw incoming list with the mapped key names//用映射的键名称覆盖原始传入列表incomingKeys transformedIncomingKeys;}//check for missing input keys//检查是否缺少输入键//查看当前的model的属性的集合是否大于传入的属性集合如果是则返回错误//也就是说模型类里的属性是不能多于传入字典里的key的例如if (![requiredProperties isSubsetOfSet:incomingKeys]) {//get a list of the missing properties//获取缺失属性的列表获取多出来的属性[requiredProperties minusSet:incomingKeys];//not all required properties are in - invalid input//并非所有必需的属性都在 in - 输入无效JMLog(Incoming data was invalid [% initWithDictionary:]. Keys missing: %, self.class, requiredProperties);if (err) *err [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];return NO;}//not needed anymore//不再需要了释放掉incomingKeys nil;requiredProperties nil;return YES;
}查值的作用主要就是为了能够检查是否model的所有property是否都能够被赋值如果不能则说明缺少值则抛出错误。这边主要就是使用了NSSet将dictionary的所有key存入一个setincomingKeys并且将key mapper映射名进行替换。将刚解析出来的model所有property的name也存入一个setrequiredProperties判断两者是不是包含关系。 此外如果存在了用户自定义的mapper则需要按照用户的定义来进行转换。
接下来我们来看initWithDictionary的第五步
__importDictionary方法
根据传入的dict进行数据的赋值如果赋值没有成功就返回nil并且抛出错误。
//作者在最后给属性赋值的时候使用的是kvc的setValue:ForKey:的方法。
//作者判断了模型里的属性的类型是否是JSONModel的子类可见作者的考虑是非常周全的。
//整个框架看下来有很多的地方涉及到了错误判断作者将将错误类型单独抽出一个类JSONModelError里面支持的错误类型很多可以侧面反应作者思维之缜密。而且这个做法也可以在我们写自己的框架或者项目中使用。
//从字典里获取值并赋给当前模型对象
-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{//loop over the incoming keys and set selfs properties//遍历保存的所有属性的字典for (JSONModelClassProperty* property in [self __properties__]) {//convert key name to model keys, if a mapper is provided//将属性的名称若有改动就拿改后的名称拿过来作为key用这个key来查找传进来的字典里对应的值NSString* jsonKeyPath (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;//JMLog(keyPath: %, jsonKeyPath);//general check for data type compliance//用来保存从字典里获取的值id jsonValue;try {jsonValue [dict valueForKeyPath: jsonKeyPath];}catch (NSException *exception) {jsonValue dict[jsonKeyPath];}//check for Optional properties//检查可选属性//字典不存在对应的keyif (isNull(jsonValue)) {//skip this property, continue with next property//跳过此属性继续下一个属性//如果这个key是可以不存在的if (property.isOptional || !validation) continue;//如果这个key是必须有的则返回错误if (err) {//null value for required property//所需属性的值为nullNSString* msg [NSString stringWithFormat:Value of required model key % is null, property.name];JSONModelError* dataErr [JSONModelError errorInvalidDataWithMessage:msg];*err [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}//获取取到的值的类型Class jsonValueClass [jsonValue class];BOOL isValueOfAllowedType NO;//查看是否是本框架兼容的属性类型for (Class allowedType in allowedJSONTypes) {if ( [jsonValueClass isSubclassOfClass: allowedType] ) {isValueOfAllowedType YES;break;}}//如果不兼容则返回NOmapping失败抛出错误if (isValueOfAllowedTypeNO) {//type not allowedJMLog(Type % is not allowed in JSON., NSStringFromClass(jsonValueClass));if (err) {NSString* msg [NSString stringWithFormat:Type % is not allowed in JSON., NSStringFromClass(jsonValueClass)];JSONModelError* dataErr [JSONModelError errorInvalidDataWithMessage:msg];*err [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}//check if theres matching property in the model//检查模型中是否有匹配的属性//如果是兼容的类型if (property) {// check for custom setter, than the model doesnt need to do any guessing// how to read the propertys value from JSON//检查自定义setter则模型不需要进行任何猜测查看是否有自定义setter并设置//如何从JSON读取属性值if ([self __customSetValue:jsonValue forProperty:property]) {//skip to next JSON key//跳到下一个JSON键continue;};// 0) handle primitivesif (property.type nil property.structNamenil) {//generic setter//通用setter//kvc赋值if (jsonValue ! [self valueForKey:property.name]) {[self setValue:jsonValue forKey: property.name];}//skip directly to the next key//直接跳到下一个键continue;}// 0.5) handle nils//如果传来的值是空即使当前的属性对应的值不是空也要将空值赋给它if (isNull(jsonValue)) {if ([self valueForKey:property.name] ! nil) {[self setValue:nil forKey: property.name];}continue;}// 1) check if property is itself a JSONModel//检查属性本身是否是jsonmodel类型if ([self __isJSONModelSubClass:property.type]) {//initialize the propertys model, store it//初始化属性的模型并将其存储//通过自身的转模型方法获取对应的值JSONModelError* initErr nil;id value [[property.type alloc] initWithDictionary: jsonValue error:initErr];if (!value) {//skip this property, continue with next property//跳过此属性继续下一个属性如果该属性不是必须的则略过if (property.isOptional || !validation) continue;// Propagate the error, including the property name as the key-path component//传播错误包括将属性名称作为密钥路径组件如果该属性是必须的则返回错误if((err ! nil) (initErr ! nil)){*err [initErr errorByPrependingKeyPathComponent:property.name];}return NO;}//当前的属性值与value不同时则赋值if (![value isEqual:[self valueForKey:property.name]]) {[self setValue:value forKey: property.name];}//for clarity, does the same without continue//为清楚起见不继续执行相同操作continue;} else {// 2) check if theres a protocol to the property// ) might or not be the case theres a built in transform for it//2检查是否有协议//可能是也可能不是它有一个内置的转换if (property.protocol) {//JMLog(proto: %, p.protocol);//转化为数组这个数组就是例子中的friends属性jsonValue [self __transform:jsonValue forProperty:property error:err];if (!jsonValue) {if ((err ! nil) (*err nil)) {NSString* msg [NSString stringWithFormat:Failed to transform value, but no error was set during transformation. (%), property];JSONModelError* dataErr [JSONModelError errorInvalidDataWithMessage:msg];*err [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}}// 3.1) handle matching standard JSON types//3.1句柄匹配标准JSON类型//对象类型if (property.isStandardJSONType [jsonValue isKindOfClass: property.type]) {//mutable properties//可变类型的属性if (property.isMutable) {jsonValue [jsonValue mutableCopy];}//set the property value//为属性赋值if (![jsonValue isEqual:[self valueForKey:property.name]]) {[self setValue:jsonValue forKey: property.name];}continue;}// 3.3) handle values to transform//3.3处理要转换的值//当前的值的类型与对应的属性的类型不一样的时候需要查看用户是否自定义了转换器例如从NSSet到NSArray转换-(NSSet *)NSSetFromNSArray:(NSArray *)arrayif ((![jsonValue isKindOfClass:property.type] !isNull(jsonValue))||//the property is mutable//属性是可变的property.isMutable||//custom struct property//自定义结构属性property.structName) {// searched around the web how to do this better// but did not find any solution, maybe thats the best idea? (hardly)//在网上搜索如何更好地做到这一点//但是没有找到任何解决方案也许这是最好的主意几乎没有Class sourceClass [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];//JMLog(to type: [%] from type: [%] transformer: [%], p.type, sourceClass, selectorName);//build a method selector for the property and json object classes//为属性和json对象类构建方法选择器NSString* selectorName [NSString stringWithFormat:%From%:,(property.structName? property.structName : property.type), //target namesourceClass]; //source nameSEL selector NSSelectorFromString(selectorName);//check for custom transformer//查看自定义的转换器是否存在BOOL foundCustomTransformer NO;if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer YES;} else {//try for hidden custom transformer//尝试隐藏自定义转换器selectorName [NSString stringWithFormat:__%,selectorName];selector NSSelectorFromString(selectorName);if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer YES;}}//check if theres a transformer with that name//检查是否有同名变压器//如果存在自定义转换器则进行转换if (foundCustomTransformer) {IMP imp [valueTransformer methodForSelector:selector];id (*func)(id, SEL, id) (void *)imp;jsonValue func(valueTransformer, selector, jsonValue);if (![jsonValue isEqual:[self valueForKey:property.name]])[self setValue:jsonValue forKey:property.name];} else {//如果没有自定义转换器返回错误if (err) {NSString* msg [NSString stringWithFormat:% type not supported for %.%, property.type, [self class], property.name];JSONModelError* dataErr [JSONModelError errorInvalidDataWithTypeMismatch:msg];*err [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}} else {// 3.4) handle all other cases (if any)// 3.4) handle all other cases (if any)//3.4处理“所有其他”情况如有if (![jsonValue isEqual:[self valueForKey:property.name]])[self setValue:jsonValue forKey:property.name];}}}}return YES;
}循环遍历model的每一个解析出来的property结构首先从dictioanry拿出真正对应property的value进行value一系列的值判断。value可用的情况下就开始进行赋值有setter方法的通过setter方法赋值基础类型intfloat等直接赋值如果property又是一个JSONModel就递归先将子Model进行整体解析。如果包含protocol字段则表明内部是一个array或者dictionary并包含这个protocol字段的对象解析。对于其他情况应该是一种类型的转换通过获取值类型和property类型调用相应的转换方法进行赋值。
作者在最后给属性赋值的时候使用的是kvc的setValue:ForKey:的方法。作者判断了模型里的属性的类型是否是JSONModel的子类可以看到作者的考虑是非常周全的。整个框架看下来有很多的地方涉及到了错误判断作者将将错误类型单独抽出一个类JSONModelError里面支持的错误类型很多体现了作者思维的缜密。而且这个做法也可以在我们写自己的框架或者项目中使用。
JSONValueTransformer的类型转化
NSMutableString - NSString
NSMutableArray - NSArray
NS(Mutable)Array - JSONModelArray
NSMutableDictionary - NSDictionary
NSSet - NSArray
BOOL - number/string
string - number
string - url
string - time zone
string - date
number - dateJSONModel的优点
命名自动匹配—-model的属性名称和服务器返回的一致比如关键字id我们可以使用keyMapper了来映射成其他的属性名称。model中可以关联其他的model只要指定对应的自身的类型model中可以集合其他的model集合这样必须要实现protocol协议在一个Model中获取服务器返回数据不同层级的数据可以设置全局键映射可以设置下划线自动转化为驼峰可以设置可选属性、忽略属性设置所有属性为可选也表示可以所有可选属性为nil可以使用内置的HTTP链接自定义数据处理内嵌转换比如类型之间的转换可以自定义处理特殊的属性可以自定义JSON的校验
带注释版JSONModel源码