JSONModel源码学习
前言
之前了解过JSONModel
的一些使用方法等,但是对于底层实现并不清楚了解,今天来学习一些JSONModel
的源码流程,本篇博客进行一个记录。
JSONModel的使用
最基础的使用
先给出一个当我们单纯传入字典的时候,转化成模型类的用法:
objc
@interface Person : JSONModel
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, assign) NSInteger age;
@end
使用字典来转换为模型:
objc
NSDictionary *dict = @{
@"name":@"Jack",
@"age":@23,
@"gender":@"male",
};
NSError *error;
Person *person = [[Person alloc] initWithDictionary:dict error:&error];
NSLog(@"%@", person);
结果
c
<Person>
[name]: Jack
[age]: 23
[gender]: male
</Person>
转换属性名称
有时传入的字典中的key发生了变化(比如说接口重构之类的原因)但是模型属性我们并不好改变,这个时候就需要有一个转化功能去修改:
这里举例将gender变为sex,那我们应该怎么操作呢
objc
+ (JSONKeyMapper *)keyMapper {
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"gender":@"sex"}];
}
来看看结果:

自定义错误
我们可以自定义属于我们自己的错误判断,比如说我们要限制Person信息的age不能小于18,需要在模型的实现文件中:
objc
- (BOOL)validate:(NSError *__autoreleasing *)error {
if (![super validate: error])
return NO;
if (self.age < 18) {
*error = [NSError errorWithDomain:@"Too young" code:10 userInfo:nil];
NSError* errorLog = *error;
NSLog(@"%@", errorLog.domain);
return NO;
}
return YES;
}
结果:

会打印错误 信息,同时也不会转化模型
模型嵌套
当我给Person类加一个朋友列表的时候,这个时候就是Person类嵌套一个Friend类,然而这种情况应该怎么操作呢:
objc
#import <Foundation/Foundation.h>
#import <JSONModel/JSONModel.h>
@protocol Friend
@end
NS_ASSUME_NONNULL_BEGIN
@interface Friend : JSONModel
@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSInteger age;
@end
@interface Person : JSONModel
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* gender;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSArray<Friend> *friends;//数组,嵌套模型
@end
NS_ASSUME_NONNULL_END
看看结果如何:

这就是几种JSONModel
比较典型的使用,下面来看看底层源码的实现。
JSONModel的继承
笔者看源码发现JSONModel需要扫描父类直至JSONModel这个类,但是嵌套的实现使用的是协议,这里记录一下笔者AI到的继承的使用,若有不对还望指正。
JSONModel的继承是通常适用于返回的数据中都有一段公共字段,我们有很多类型数据的时候,他们都有id和created_at,那么我们可以创建一个BaseModel
,令所有类型的model直接继承这个类型model,这样我们可以避免每个子类重写。
源码实现
先来一张流程图

这里大致讲解一下流程:
首先在这个模型类的对象被初始化的时候,遍历自身到所有的父类,获取所有的属性,将其保存到一个字典中去,获取传入字典的所有的key,将这些key同保存的属性进行匹配。若是匹配成功,就进行KVC赋值
JSONModel
一共提供了四种初始化的方法,如下:
objc
-(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;
这里我们从-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
这种最经典的方法讲起:
initWithDictionary
先来看看这个方法的源码实现:
objc
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
//检查参数是否为nil
if (!dict) {
if (err) *err = [JSONModelError errorInputIsNil];
return nil;
}
//参数不是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;
}
//初始化
self = [self init];
if (!self) {
//super init didn't succeed
if (err) *err = [JSONModelError errorModelIsInvalid];
return nil;
}
//检查用户定义的模型里的属性集合是否大于传入的字典里的key集合(如果大于,则返回NO)
if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
return nil;
}
//字典的key与模型的属性的映射
if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
return nil;
}
//可以重写[self validate:err]方法并返回No,令用户自定义错误去阻拦model的返回
if (![self validate:err]) {
return nil;
}
//model is valid! yay!
return self;
}
总结:
- 1-4步中都是对错误的发现和处理
- 方法5是真正的mapping
- 方法6是作者自定义错误的方法,若是复合了自定义的错误,即使mapping成功也要返回nil
- 方法7成功返回模型对象
在开始之前,先来了解一下JSONModel
所持有的数据:
objc
static const char * kMapperObjectKey;//自定义的mapper,具体使用方法在上面的例子
static const char * kClassPropertiesKey;//用来保存所有属性信息的NSDictionary
static const char * kClassRequiredPropertyNamesKey;//用来保存所有属性的名称NSSet
static const char * kIndexPropertyNameKey;
KeyMapper的使用
JSONModel 提供了一个叫做 JSONKeyMapper 的工具类,用于在 JSON 数据中查找与类属性名相对应的属性名,以便进行正确的映射。
JSONKeyMapper 是一个可定制的映射器,它提供了两种映射方式:
- 下划线式(UnderscoreCase)映射:将下划线形式的 JSON 数据中的属性名转换成类属性名(如:foo_bar -> fooBar)。
- 驼峰式(CamelCase)映射:将驼峰形式的 JSON 数据中的属性名转换成类属性名(如:fooBar -> foo_bar)。
objc
JSONKeyMapper *mapper = [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{
@"propertyOne": @"property_one",
@"propertyTwo": @"property_two"
}];
MyModel *model = [[MyModel alloc] initWithDictionary:jsonDict error:nil];
init
我们从第三个方法init开始,看看流程:
objc
-(id)init
{
self = [super init];
if (self) {
//do initial class setup
[self __setup__];
}
return self;
}
-(void)__setup__
{
//如果第一次实例化,就执行
if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
[self __inspectProperties];
}
//若是存在自定义的mapper,就将其保存在关联对象中,key是KMapperObjectKey
id mapper = [[self class] keyMapper];
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
objc_setAssociatedObject(
self.class,
&kMapperObjectKey,
mapper,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
}
这里我们使用
!objc_getAssociatedObject(self.class, &kMapperObjectKey)
的时候第一个参数使用self.class
,这就是说这里我们获取当前类的关联对象,要是关联对象不存在,就说明当前类还没有被解析过,需要调用__inspectProperties
方法进行解析
在第一次实例化的时候,所调用的__inspectProperties
也是该框架的核心方法之一:其保存了所有需要赋值的属性,用作在将来与传进来字典进行映射:
具体来说,该方法会使用运行时特性获取模型类的属性列表,并为每个属性创建一个 JSONModelProperty
对象,该对象包含属性名、数据类型、对应的 JSON 字段名等信息。然后,这些 JSONModelProperty
对象将存储在一个 NSMutableDictionary
对象中,以属性名作为键,JSONModelProperty
对象作为值。最后,该 NSMutableDictionary
对象将使用 objc_setAssociatedObject
方法与模型类关联起来,以便以后可以方便地访问。
objc
-(void)__inspectProperties
{
// 最终保存所有属性的字典,形式为:
// {
// 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];
//获取当前的类名
Class class = [self class];
NSScanner* scanner = nil;
NSString* propertyType = nil;
// 循环条件:当class是JSONModel自己的时候会终止
while (class != [JSONModel class]) {
//JMLog(@"inspecting: %@", NSStringFromClass(class));
//所有属性的个数
unsigned int propertyCount;
//获取属性列表
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
///遍历所有的属性
for (unsigned int i = 0; i < propertyCount; i++) {
//获得属性名称
objc_property_t property = properties[i];//获得当前的属性
const char *propertyName = property_getName(property);//name(C字符串)
//JSONModel里的每一个属性,都被封装成一个JSONModelClassProperty对象
JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
p.name = @(propertyName);//propertyName:属性名称,例如:name,age,gender
//获得属性类型
const char *attrs = property_getAttributes(property);
NSString* propertyAttributes = @(attrs);
// T@\"NSString\",C,N,V_name
// Tq,N,V_age
// T@\"NSString\",C,N,V_gender
// T@"NSArray",&,N,V_friends
NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];
//说明是只读属性,不做任何操作
if ([attributeItems containsObject:@"R"]) {
continue; //to next property
}
//检查出是布尔值
if ([propertyAttributes hasPrefix:@"Tc,"]) {
p.structName = @"BOOL";//使其变为结构体
}
//实例化一个scanner
scanner = [NSScanner scannerWithString: propertyAttributes];
[scanner scanUpToString:@"T" intoString: nil];
[scanner scanString:@"T" intoString:nil];
if ([scanner scanString:@"@\"" intoString: &propertyType]) {
//属性是一个对象
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
intoString:&propertyType];//propertyType -> NSString
p.type = NSClassFromString(propertyType);// p.type = @"NSString"
p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound); //判断是否是可变的对象
p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];//是否是该框架兼容的类型
//存在协议(数组,也就是嵌套模型)
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];
}
}
else if ([scanner scanString:@"{" intoString: &propertyType])
//属性是结构体
[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
intoString:&propertyType];
p.isStandardJSONType = NO;
p.structName = propertyType;
}
else {
//属性是基本类型:Tq,N,V_age
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
intoString:&propertyType];
//propertyType:q
propertyType = valueTransformer.primitivesNames[propertyType];
//propertyType:long
//基本类型数组
if (![allowedPrimitiveTypes containsObject:propertyType]) {
//类型不支持
@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;
}
//集合类
Class customClass = [[self class] classForCollectionProperty:nsPropertyName];
if (customClass) {
p.protocol = NSStringFromClass(customClass);
}
//忽略block
if ([propertyType isEqualToString:@"Block"]) {
p = nil;
}
//如果字典里不存在,则添加到属性字典里(终于添加上去了。。。)
if (p && ![propertyIndex objectForKey:p.name]) {
[propertyIndex setValue:p forKey:p.name];
}
//setter 和 getter
if (p)
{ //name ->Name
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);
//再指向自己的父类,知道等于JSONModel才停止
class = [class superclass];
}
//最后保存所有当前类,JSONModel的所有的父类的属性
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN
);//使用关联对象与模型类关联起来
}
这里有几点需要注意:
- 作者使用一个
while
函数,获取当前类和当前类的除去JSONModel
的所有父类的属性保存在一个字典中。用来和传入的字典进行一个映射关系。 - 作者使用
JSONModelClassProperty
类封装了JSONModel
的每一个属性,这个类有两个重要的属性:一个是name,这是属性的名称;另一个是type,这是属性的类型 - 作者将属性分为了以下几个类型:
- 对象(不含有协议)
- 对象(含有协议,即模型嵌套)
- 基本数据类型
- 结构体
__doesDictionary
objc
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{
//拿到字典中所有的key
NSArray* incomingKeysArray = [dict allKeys];
//返回保存所有属性名称的数组(name,age,gender...)
NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;
//从array中拿到set
NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
//若是用户自定义了mapper,进行一个转换
if (keyMapper || globalKeyMapper) {
NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
NSString* transformedName = nil;
//遍历需要转换的属性列表
for (JSONModelClassProperty* property in [self __properties__]) {
//被转换成的属性名称 gender(模型内) -> sex(字典内)
transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
//拿到sex以后,查看传入的字典里是否有sex对应的值
@try {
value = [dict valueForKeyPath:transformedName];
}
@catch (NSException *exception) {
value = dict[transformedName];
}
//若是值存在,就将sex添加到传入的keys数组中
if (value) {
[transformedIncomingKeys addObject: property.name];
}
}
//用映射的键名称覆盖原始传入列表
incomingKeys = transformedIncomingKeys;
}
//查看当前的model的属性的集合是否大于传入的属性集合,如果是,则返回错误。
//也就是说模型类里的属性是不能多于传入字典里的key的,例如:
if (![requiredProperties isSubsetOfSet:incomingKeys]) {
//获取缺失属性的列表(获取多出来的属性)
[requiredProperties minusSet:incomingKeys];
//并非所有必需的属性都在 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;
}
- 这个方法就将我们使用过程中重写的
keyMapper
方法中的属性名称进行了一个转换,按照我们的需求进行一个改变 - 这里我们可以看到在最后一个if判断中说明,我们传入的字典数据中的key集合不能小于model类的定义的属性集合,也就是说我们model类中定义的属性集合必须要赋值。
importDictionary
终于到了将NSDictionary
对象转化为JSONModel
对象这一步了,来看看会发生什么:
objc
-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{
//遍历保存的所有属性的字典
for (JSONModelClassProperty* property in [self __properties__]) {
//将属性的名称拿过来,作为key,用这个key来查找传进来的字典里对应的值
NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;
//用来保存从字典里获取的值
id jsonValue;
@try {
jsonValue = [dict valueForKeyPath: jsonKeyPath];
}
@catch (NSException *exception) {
jsonValue = dict[jsonKeyPath];
}
//字典不存在对应的key
if (isNull(jsonValue)) {
//如果这个key是可以不存在的
if (property.isOptional || !validation) continue;
//如果这个key是必须有的,则返回错误
if (err) {
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;
}
//如果是兼容的类型:
if (property) {
// 查看是否有自定义setter,并设置
if ([self __customSetValue:jsonValue forProperty:property]) {
continue;
};
// 基本类型
if (property.type == nil && property.structName==nil) {
//kvc赋值
if (jsonValue != [self valueForKey:property.name]) {
[self setValue:jsonValue forKey: property.name];
}
continue;
}
// 如果传来的值是空,即使当前的属性对应的值不是空,也要将空值赋给它
if (isNull(jsonValue)) {
if ([self valueForKey:property.name] != nil) {
[self setValue:nil forKey: property.name];
}
continue;
}
// 1. 属性本身是否是jsonmodel类型
if ([self __isJSONModelSubClass:property.type]) {
//通过自身的转模型方法,获取对应的值
JSONModelError* initErr = nil;
id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];
if (!value) {
//如果该属性不是必须的,则略过
if (property.isOptional || !validation) continue;
//如果该属性是必须的,则返回错误
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];
}
continue;
} else {
// 如果不是jsonmodel的类型,则可能是一些普通的类型:NSArray,NSString。。。
// 是否是模型嵌套(带有协议)
if (property.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;
}
}
// 对象类型
if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
//可变类型
if (property.isMutable) {
jsonValue = [jsonValue mutableCopy];
}
//赋值
if (![jsonValue isEqual:[self valueForKey:property.name]]) {
[self setValue:jsonValue forKey: property.name];
}
continue;
}
// 当前的值的类型与对应的属性的类型不一样的时候,需要查看用户是否自定义了转换器(例如从NSSet到NSArray转换:- (NSSet *)NSSetFromNSArray:(NSArray *)array)
if (
(![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
||
//the property is mutable
property.isMutable
||
//custom struct property
property.structName
) {
Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];
//JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);
NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",
(property.structName? property.structName : property.type), //target name
sourceClass]; //source name
SEL selector = NSSelectorFromString(selectorName);
//查看自定义的转换器是否存在
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;
}
}
//如果存在自定义转换器,则进行转换
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 {
//没有自定义转换器,返回错误
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)
if (![jsonValue isEqual:[self valueForKey:property.name]])
[self setValue:jsonValue forKey:property.name];
}
}
}
}
return YES;
}
值得注意的是:
- 作者在最后给属性赋值的时候使用的是kvc的
setValue:ForKey:
的方法。 - 作者判断了模型里的属性的类型是否是JSONModel的子类,可见作者的考虑是非常周全的。
- 整个框架看下来,有很多的地方涉及到了错误判断,作者将将错误类型单独抽出一个类(
JSONModelError
),里面支持的错误类型很多。
在设置属性值时,__importDictionary方法还会进行一些类型转换和校验
- 如果属性是JSONModel类型,则将字典转换为该类型的JSONModel对象,并设置到当前属性中。如果属性是基本数据类型(如int、float等),则将字典中的数值转换为相应的数据类型,并设置到当前属性中。
- 如果属性是NSDate类型,则将字典中的时间戳转换为NSDate对象,并设置到当前属性中。总之,__importDictionary方法的主要作用是将NSDictionary对象转换为JSONModel对象,并进行一些类型转换和校验。
这里有一点笔者之前了解不够的地方记录一下:
objc@property (nonatomic, strong) NSString<Optional> *optionalString;
当我们使用
<Optional>
这个协议标记的时候,说明这个属性不是必须实现的,当我们没有实现这个属性的时候,其被赋值为nil
优点
- Runtime动态解析model数据类型
- keyMapper映射
- KVC赋值
- 使用关联对象来避免重复解析相同模型,很妙