JSONModel是我们要讲解的重点,我们首先从它的初始化方法谈起。
-(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
-(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
-(instancetype)initWithData:(NSData *)data error:(NSError **)error;
designated initializer
粗略一看,四个初始化方法,太可怕了。但是我们知道在iOS的设计理念里,有一种designated initializer的说法
在自己的开发过程中,合理地遵守和运用designated initializer会减少许多重复代码。
并且理解了这一个概念,对整个Cocoa框架的理解也有帮助。 例如UIViewController的Designated initializer是
- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
但是可能有人会发现,如果你直接使用 [[viewController alloc]init]来生产Controller,且你是使用XIB来组织界面的,那么最后你得到的ViewController的View还是来自XIB的。这背后的原因就是Designated initializer帮你完成了这个工作。
因此,我们挑一个 initWithDictionary看起。
废话不多说直接看源码
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
//检查是否为空
if (!dict) {
if (err) *err = [JSONModelError errorInputIsNil];
return nil;
}
//类型判断
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
self = [self init];//转入重写的init方法
。。。。。。。后面的在init之后,先看init方法
}
**************************************************************************************************************
//重写的init方法
-(id)init
{
self = [super init];
if (self) {
//do initial class setup
[self __setup__];
}
return self;
}
**************************************************************************************************************
-(void)__setup__
{
//if first instance of this model, generate the property list
if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {//是否第一次初始化这个模型类,标准是是否绑定了kClassPropertiesKey,kClassPropertiesKey取出来是数组(存放类的成员变量)
[self __inspectProperties];//去遍历成员变量列表,并把遍历好的放到数组中,以kClassPropertiesKey为键绑定:objc_setAssociatedObject,
}
//if there's a custom key mapper, store it in the associated object
id mapper = [[self class] keyMapper];//取出需要进行转换的成员变量名,比如id-->ID,是重写keyMapper返回的JSONKeyMapper类型
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {//如果有,并且是第一次初始化之前没有进行过绑定
objc_setAssociatedObject(
self.class,
&kMapperObjectKey,
mapper,
OBJC_ASSOCIATION_RETAIN // This is atomic
);//绑定到类上
}
}
这里需要注意的是 objc_setAssociatedObject
这个方法 之前可能很少用到。简单来说类目是给对象增加方法 objc_setAssociatedObject是为类或者对象增加属性
创建关联要使用到Objective-C的运行时函数:objc_setAssociatedObject
来把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象和一个关联策略。当然,此处的关键字和关联策略是需要进一步讨论的。
■ 关键字是一个void
类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
■ 关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。 下面是实例代码
//
// main.m
// objc_getAssociatedObject
//
// Created by 何壮壮 on 16/11/4.
// Copyright © 2016年 何壮壮. All rights reserved.
//
#import
#import "AppDelegate.h"
#import
#import
#import "person.h"
// 表示关联关系的key,主要目的是用来索引
const NSString *associatedKey = @"associate_nsarray_with_nsstring_key";
int main(int argc, char * argv[]) {
@autoreleasepool {
NSArray *array = [[NSArray alloc]initWithObjects:@"1", @"2", @"3", nil];
NSString * overview = @"Hello world";
// 从array中获取被关联的对象string
// 注意,这里就没有string这个对象任何事了
// string其实已经变成了array的一个属性值
objc_setAssociatedObject(array, &associatedKey, overview, OBJC_ASSOCIATION_RETAIN);
NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &associatedKey);
NSLog(@"associatedObject:%@",associatedObject);
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
这段代码使用AssociateObject的缓存判断kClassPropertiesKey就知道该model对象是否有进行过解析property,没有的话进行解析,同时取出model的key mapper,也同样进行缓存。
key mapper主要是用来针对某些json字段名和model数据名不一致的情况。
比如"com.app.test.name":"xxx","test_name":"xxx"这样的情况,可能对应的model数据字段名为name,那如何讲着两个值进行映射,就通过key mapper来完成。
主体的解析代码如下:
遍历成员变量列表并设置关联
-(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));
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);//取出成员变量列表
//loop over the class properties
for (unsigned int i = 0; i < propertyCount; i++) {
//JSONModelClassProperty包涵解析与赋值时候的所有判断
JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
//get property name
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
p.name = @(propertyName);//成员变量名
//JMLog(@"property: %@", p.name);
//get property attributes
//核心,通过property_getAttributes获取property的encode string,解析encode string可以解析出具体property的类型
const char *attrs = property_getAttributes(property);//取出成员变量属性
NSString* propertyAttributes = @(attrs);
NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];
//ignore read-only properties 包含 R 的为只读的,不解析
if ([attributeItems containsObject:@"R"]) {
continue; //to next property
}
//check for 64b BOOLs 检查是否是c中字符char ,char 的头是Tc
if ([propertyAttributes hasPrefix:@"Tc,"]) {
//mask BOOLs as structs so they can have custom convertors
p.structName = @"BOOL";
}
scanner = [NSScanner scannerWithString: propertyAttributes];
//JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
//把scanner的scanLocation(我理解为光标)移动到T之后
[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]) {
//继承自NSObject的类会有 @/" 前缀,例如 "T@\"NSString\",C,N,V_type",,移动到@/"之后
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
intoString:&propertyType];//扫描到"<(有遵循的协议)或者"(没有遵循的协议)之后,这里的协议只指声明成员变量的时候声明遵循的协议,得到的就是class类,<>里面是遵循的协议,见上面例子
//JMLog(@"type: %@", propertyClassName);
p.type = NSClassFromString(propertyType);
p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);//是否是可变的
p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];//是否是允许的类allowedJSONTypes = @[NSString class], [NSNumber class], [NSDecimalNumber class], [NSArray class], [NSDictionary class], [NSNull class],[NSMutableString class], [NSMutableArray class], [NSMutableDictionary class]];
//read through the property protocols
while ([scanner scanString:@"<" intoString:NULL]) {//扫描遵循的协议,一个协议一对<>,是否可选、忽略之类的
NSString* protocolName = nil;
[scanner scanUpToString:@">" intoString: &protocolName];
if ([protocolName isEqualToString:@"Optional"]) {
p.isOptional = YES;
} else if([protocolName isEqualToString:@"Index"]) {
p.isIndex = YES;
objc_setAssociatedObject(
self.class,
&kIndexPropertyNameKey,
p.name,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
} else if([protocolName isEqualToString:@"ConvertOnDemand"]) {
p.convertsOnDemand = YES;
} else if([protocolName isEqualToString:@"Ignore"]) {
p = nil;
} else {
p.protocol = protocolName;
}
[scanner scanString:@">" intoString:NULL];
}
}
//check if the property is a structure
else if ([scanner scanString:@"{" intoString: &propertyType]) {//或者是结构体,CGPoint,CGRect之类的,具体成员变量里为什么会出现这个现在还没有遇到过,官方文档说包含{的是:@property struct YorkshireTeaStruct structDefault;T{YorkshireTeaStruct="pot"i"lady"c},VstructDefault/@property YorkshireTeaStructType typedefDefault;T{YorkshireTeaStruct="pot"i"lady"c},VtypedefDefault/@property union MoneyUnion unionDefault;T(MoneyUnion="alone"f"down"d),VunionDefault
[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
intoString:&propertyType];
p.isStandardJSONType = NO;
p.structName = propertyType;
}
//the property must be a primitive
else {//原始 数据类型,允许的原始数据类型allowedPrimitiveTypes = @[@"BOOL", @"float", @"int", @"long", @"double", @"short",@"NSInteger", @"NSUInteger", @"Block"];
//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];//取出基本数据类型的全称@{@"f":@"float", @"i":@"int", @"d":@"double", @"l":@"long", @"c":@"BOOL", @"s":@"short", @"q":@"long",@"I":@"NSInteger", @"Q":@"NSUInteger", @"B":@"BOOL",@"@?":@"Block"};
//判断是否是允许的基本数据类型
if (![allowedPrimitiveTypes containsObject:propertyType]) {
//type not allowed - programmer mistaked -> exception
@throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"
reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]
userInfo:nil];
}
}
NSString *nsPropertyName = @(propertyName);
//基本数据类型定义的时候不能遵循协议,通过类方法判断成员变量遵循的协议
if([[self class] propertyIsOptional:nsPropertyName]){
p.isOptional = YES;
}
if([[self class] propertyIsIgnored:nsPropertyName]){
p = nil;
}
NSString* customProtocol = [[self class] protocolForArrayProperty:nsPropertyName];
if (customProtocol) {
p.protocol = customProtocol;
}
//few cases where JSONModel will ignore properties automatically 忽略block的成员变量
if ([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];
}
}
free(properties);//注意:这里释放properties,否则会造成内存泄露
//ascend to the super of the class
//(will do that until it reaches the root class - JSONModel)
//继续遍历直到基类JSONMedol
class = [class superclass];
}
//finally store the property index in the static property index
//将遍历的结果绑定到类上,这样保证只遍历一次
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
这么多代码,其实总结起来就几个步骤:
获取当前类的的property list,通过class_copyPropertyList runtime的方法
遍历每一个propery,解析他们的属性,这里的属性包括是否只读、类型、是否是weak类型,是否是原子性的等等,如果不了解,可以看如下的表格:
| Code | Meaning |
| :————-: |:————-
| R | The property is read-only (readonly).
| C | The property is a copy of the value last assigned (copy).
| & | The property is a reference to the value last assigned (retain).
| N | The property is non-atomic (nonatomic).
| G| The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).
| S| The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,).
| D | The property is dynamic (@dynamic).
| W | The property is a weak reference (__weak).
| P | The property is eligible for garbage collection.
| t| Specifies the type using old-style encoding.
根据解析结果,检测是不是合法,如果合法创建对应的JSONModelClassProperty并赋值相应的属性值。
然后重复执行,查看完当前的类就去查询其父类,直到没有为止。
最后将解析出来的property list通过Associate Object给赋值,这和我们刚刚在 setup中看到的相呼应。
简单来说通过**class_copyPropertyList
获取属性列表,得到objc_property_t
**数组。
You can use the functions class_copyPropertyList and protocol_copyPropertyList to retrieve an array of the properties associated with a class (including loaded categories) and a protocol respectively
通过遍历**objc_property_t
调用property_getName
获得名称,property_getAttributes
**获得属性的描述(字符串)。
You can use the property_getAttributes function to discover the name and the @encode type string of a property.
存储信息。
最后class = [class superclass]
获取父类,如果父类是JSONModel的子类,继续进行上述三步。
最后的最后objc_setAssociatedObject
字典MatchingModel确保是KeyMapper的子集
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{
//check if all required properties are present
NSArray* incomingKeysArray = [dict allKeys];
NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;//[self __requiredPropertyNames]会对__properties__遍历取出isOptional=NO的,并且通过objc_setAssociatedObject,这样可以只在第一次遍历,后面直接取
NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
//transform the key names, if neccessary
//处理有需要转换名字的成员属性
if (keyMapper || globalKeyMapper) {
NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
NSString* transformedName = nil;
//loop over the required properties list
for (JSONModelClassProperty* property in [self __properties__]) {
transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
//chek if exists and if so, add to incoming keys
id value;
@try {//这里没有懂为什么用valueForKeyPath:,没有用valueForKey:
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
//利用NSSet的子集 看看要求必有的是否是传入字典的子集
if (![requiredProperties isSubsetOfSet:incomingKeys]) {
//get a list of the missing properties
//取出没有的成员列表变量,如果有error,赋给error,并返回NO
[requiredProperties minusSet:incomingKeys];
//not all required properties are in - invalid input
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;
}
如果得到的objc_property_t
数组是dict
的超集,说明我们的Model中有属性是赋不到值的,往往程序就会crash在这里。解决方法,属性后加入
表示可以为空。或者
@implementation ProductModel
+(BOOL)propertyIsOptional:(NSString*)propertyName
{
return YES;
}
@end
在json数据中例如有字段名为id或者description的语言关键字,语法中式不能这么写的
@property (assign, nonatomic) int id;
只能写这种
@property (assign, nonatomic) int xxxID;
而这么写了在此func种就会由于**objc_property_t
中不包含xxxID
**crash,于是就有了keyMapper可以避免这个问题。
//赋值
-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{
//loop over the incoming keys and set self's properties
for (JSONModelClassProperty* property in [self __properties__]) {
//convert key name ot model keys, if a mapper is provided
NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
//JMLog(@"keyPath: %@", jsonKeyPath);
//general check for data type compliance
id jsonValue;//从jsonDict中以property取值
@try {
jsonValue = [dict valueForKeyPath: jsonKeyPath];
}
@catch (NSException *exception) {
jsonValue = dict[jsonKeyPath];
}
//check for Optional properties
//取出数据为空
if (isNull(jsonValue)) {
//skip this property, continue with next property
//如果是可选的继续,
if (property.isOptional || !validation) continue;
if (err) {
//null value for required property
NSString* 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;
}
}
//不是允许的数据类型,报错
if (isValueOfAllowedType==NO) {
//type not allowed
JMLog(@"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 there's matching property in the model
if (property) {
// check for custom setter, than the model doesn't need to do any guessing
// how to read the property's value from JSON
//检查成员变量的set方法,property.setterType默认为kNotInspected(未检查过的),还有kNo(没有set方法),kCustom(正常的set方法,即:setPropertnameWith:),如果有赋值,并返回yes
if ([self __customSetValue:jsonValue forProperty:property]) {
//skip to next JSON key
//有正常的set赋值方法,赋值,continue
continue;
};
// 0) handle primitives
//赋值基本数据类型:不是对象也不是结构体,int、float之类的
if (property.type == nil && property.structName==nil) {
//generic setter
if (jsonValue != [self valueForKey:property.name]) {
[self setValue:jsonValue forKey: property.name];
}
//skip directly to the next key
continue;
}
// 0.5) handle nils
//判断是否为nil,或者NSNull类型
if (isNull(jsonValue)) {
//字典值为空,self之前的值非空,赋空
if ([self valueForKey:property.name] != nil) {
[self setValue:nil forKey: property.name];
}
continue;
}
// 1) check if property is itself a JSONMode
if ([self __isJSONModelSubClass:property.type]) {
//处理jsonModel嵌套
//initialize the property's 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;
}
//判断是否相等,否则重新赋值
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 there's a protocol to the property
// ) might or not be the case there's a built in transofrm for it
if (property.protocol) {
//JMLog(@"proto: %@", p.protocol);
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
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
if (
(![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 that's 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
NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",
(property.structName? property.structName : property.type), //target name
sourceClass]; //source name
SEL 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 there's a transformer with that name
if (foundCustomTransformer) {
//it's OK, believe me...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
//transform the value
jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];
#pragma clang diagnostic pop
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
} else {
// it's not a JSON data type, and there's no transformer for it
// if property type is not supported - that's a programmer mistaked -> exception
@throw [NSException exceptionWithName:@"Type not allowed"
reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name]
userInfo:nil];
return NO;
}
} else {
// 3.4) handle "all other" cases (if any)
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
}
}
}
}
return YES;
}
整个func主要就做赋值工作
从对象的 **properties
** 数组中,循环到 **dict
** 中取值,通过 **KVC
** 操作。循环中,使用 **keyMapper
** 的映射找对对应的字典属性的 **keyPath
** ,获取 **jsonValue
**
ps:因为是通过KVO赋值,根本是使用keyPath,所以keyMapper可以解决当model中属性名和json串中属性名不同的问题
赋值时分以下几种情况:
- 判断是不是空值,如果是空值并且属性非optional,error。
- 判断是不是合法的json类型,如果不是,error。
- 如果是属性:
3.1 如果有自定义setter调用自定义setter,然后continue。
3.2 如果没有自定setter时,属性是基本数据类型,int,double等,直接用KVC赋值。
3.3 如果没有自定setter时,属性是一个JSONModel,就解析这个JSONModel(递归),最终还是使用KVC赋值。
3.4 如果没有自定setter时,属性带有protocol字段,说明是个字典或者数组,将他遍历后解析。
3.5 如果没有自定setter时,属性和json串中字段均为标准json类型,直接用KVC赋值。
3.6 如果没有自定setter时,属性和json串中字段不同,使用JSONValueTransformer进行转换。
3.7 处理其他case,使用KVC直接赋值。
首先在OC中拥有很多簇类。
当我们debug的时候有时会发现一个NSString在底层不是NSString,有时是NSPlaceholderString,有时又是别的。
因为NSString在设计上得时候采用了抽象工厂的设计模式,内部是一个簇类(class cluster)。
这也是NSString,NSArray,NSDictionary什么的官方不建议去继承的原因。
使用JSONValueTransformer,就是由于这些簇类。需要使用JSONValueTransformer得到真正的类型。
关于类簇以及抽象工厂模式可以看这篇文章
http://www.jianshu.com/p/0dfd1b66233a