JSONModel

文章目录

JSONModel关于JSONModel的用法initWithDictionary等方法load方法实现load方法调用时机

init方法__setup__方法__inspectProperties:方法

__doesDictionary方法__importDictionary方法JSONModel所有类的作用简述:1.JSONModel类2.JSONModelError类3.JSONValueTransformer协议4.JSONKeyMapper类

关于JSONModel的用法

可以参考之前写的博客:【iOS】—— JSONModel使用以及Manager封装网络请求

initWithDictionary等方法

整个过程用到的变量:

#pragma mark - associated objects names //关联对象名称

static const char * kMapperObjectKey;

//关联对象kMapperObjectKey,保存自定义的mapper

static const char * kClassPropertiesKey;

//关联对象kClassPropertiesKey,用来保存所有属性信息的NSDictionary

static const char * kClassRequiredPropertyNamesKey;

//关联对象kClassRequiredPropertyNamesKey,用来保存所有属性的名称NSSet

static 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 input

if (!data) {

if (err) *err = [JSONModelError errorInputIsNil];

return nil;

}

//read the json

JSONModelError* initError = nil;

id obj = [NSJSONSerialization JSONObjectWithData:data

options:kNilOptions

error:&initError];

if (initError) {

if (err) *err = [JSONModelError errorBadJSON];

return nil;

}

//init with dictionary

id 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 input

if (!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.第一步判断传入的是否为nil

if (!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方法初始化映射property

self = [self init];

if (!self) {

//super init didn't succeed

if (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.前面的判断都通过,返回self

return 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 subclasses

@autoreleasepool {

//兼容的对象属性

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 aliases

@"NSInteger", @"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 there's 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, get's 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); //name(c字符串)

p.name = @(propertyName); //propertyNeme:属性名称,例如:name,age,gender

//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的string

while ([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-declarations"

p.isIndex = YES;

#pragma GCC diagnostic pop

objc_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;检查属性是否为structure

else 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 allowed"

reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]

userInfo:nil];

}

}

NSString *nsPropertyName = @(propertyName);

// 判断property是不是Optional

if([[self class] propertyIsOptional:nsPropertyName]){

p.isOptional = YES;

}

// 判断property是不是Ignored

if([[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会自动忽略属性的情况,忽略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];

}

// generate custom setters and getter

//生成自定义setter和getter

if (p)

{

//name -> Name(大写p的名字属性的前两个字母)

NSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];

// getter

SEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);

if ([self respondsToSelector:getter])

p.customGetter = getter;

// setters

p.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拿到set

NSSet* 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存入一个set:incomingKeys,并且将key mapper映射名进行替换。将刚解析出来的model所有property的name也存入一个set:requiredProperties,判断两者是不是包含关系。 此外,如果存在了用户自定义的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 self's 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

//检查可选属性

//字典不存在对应的key

if (isNull(jsonValue)) {

//skip this property, continue with next property

//跳过此属性,继续下一个属性

//如果这个key是可以不存在的

if (property.isOptional || !validation) continue;

//如果这个key是必须有的,则返回错误

if (err) {

//null value for required property

//所需属性的值为null

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;

}

}

//如果不兼容,则返回NO,mapping失败,抛出错误

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

//检查自定义setter,则模型不需要进行任何猜测(查看是否有自定义setter,并设置)

//如何从JSON读取属性值

if ([self __customSetValue:jsonValue forProperty:property]) {

//skip to next JSON key

//跳到下一个JSON键

continue;

};

// 0) handle primitives

if (property.type == nil && property.structName==nil) {

//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 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;

}

//当前的属性值与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 there's a protocol to the property

// ) might or not be the case there's 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 *)array)

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

//为属性和json对象类构建方法选择器

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) {

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方法赋值,基础类型int,float等直接赋值,如果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 <-> date

JSONModel的优点:

命名自动匹配—-model的属性名称和服务器返回的一致,比如关键字id我们可以使用keyMapper了来映射成其他的属性名称。model中可以关联其他的model,只要指定对应的自身的类型model中可以集合其他的model集合,这样必须要实现@protocol协议在一个Model中获取服务器返回数据不同层级的数据可以设置全局键映射可以设置下划线自动转化为驼峰可以设置可选属性、忽略属性设置所有属性为可选也表示可以所有可选属性为nil可以使用内置的HTTP链接自定义数据处理,内嵌转换比如类型之间的转换可以自定义处理特殊的属性可以自定义JSON的校验

带注释版JSONModel源码

JSONModel所有类的作用简述:

1.JSONModel类

JSONModel类是JSONModel框架的核心部分,它实现了将JSON数据解析成Objective-C对象的功能。在JSONModel中,每个Objective-C类都对应一个JSON数据模型。JSONModel通过使用NSObject的categories来扩展NSObject的功能,并添加支持JSON数据解析的方法。

JSONModel使用@property声明来定义JSONModel对象的属性。这些属性对应于JSON数据中的键值对。例如,如果JSON数据中有一个"name"键,则对应的属性名称应该是"name"。

2.JSONModelError类

JSONModelError类是JSONModel中的一个错误类,它用于在解析JSON数据时发生错误时返回错误信息。如果JSONModel无法将JSON数据转换为Objective-C对象,则会生成一个JSONModelError对象,并通过该对象返回相应的错误信息。

3.JSONValueTransformer协议

JSONValueTransformer协议定义了一组转换方法,用于将JSON数据转换为Objective-C对象及其反向过程。在JSONModel中,所有实现了JSONValueTransformer协议的类都可以作为value transformer使用。

4.JSONKeyMapper类

JSONKeyMapper类提供了一种自定义映射JSON属性名和Objective-C属性名的方式。在某些情况下,JSON数据中的键名可能与Objective-C属性名称不匹配。此时可以通过重写+(JSONKeyMapper *)keyMapper方法来实现自定义映射。在该方法中,可以指定每个JSON属性名与Objective-C属性名之间的映射关系。

总体而言,JSONModel提供了一种强大且易于使用的方式来处理iOS应用程序中的JSON数据。它提供了类型安全、易于扩展和高度可定制的功能,使得开发人员能够更快速、更轻松地开发高质量的iOS应用程序。

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: