JSONModel源碼學習
- 前言
- JSONModel的使用
- 最基礎的使用
- 轉換屬性名稱
- 自定義錯誤
- 模型嵌套
- JSONModel的繼承
- 源碼實現
- initWithDictionary
- init
- __doesDictionary
- importDictionary
- 優點
前言
之前了解過JSONModel
的一些使用方法等,但是對于底層實現并不清楚了解,今天來學習一些JSONModel
的源碼流程,本篇博客進行一個記錄。
JSONModel的使用
最基礎的使用
先給出一個當我們單純傳入字典的時候,轉化成模型類的用法:
@interface Person : JSONModel@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, assign) NSInteger age;@end
使用字典來轉換為模型:
NSDictionary *dict = @{@"name":@"Jack",@"age":@23,@"gender":@"male",};NSError *error;Person *person = [[Person alloc] initWithDictionary:dict error:&error];
NSLog(@"%@", person);
結果
<Person> [name]: Jack[age]: 23[gender]: male
</Person>
轉換屬性名稱
有時傳入的字典中的key發生了變化(比如說接口重構之類的原因)但是模型屬性我們并不好改變,這個時候就需要有一個轉化功能去修改:
這里舉例將gender變為sex,那我們應該怎么操作呢
+ (JSONKeyMapper *)keyMapper {return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"gender":@"sex"}];
}
來看看結果:
自定義錯誤
我們可以自定義屬于我們自己的錯誤判斷,比如說我們要限制Person信息的age不能小于18,需要在模型的實現文件中:
- (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類,然而這種情況應該怎么操作呢:
#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;//數組,嵌套模型
@endNS_ASSUME_NONNULL_END
看看結果如何:
這就是幾種JSONModel
比較典型的使用,下面來看看底層源碼的實現。
JSONModel的繼承
筆者看源碼發現JSONModel需要掃描父類直至JSONModel這個類,但是嵌套的實現使用的是協議,這里記錄一下筆者AI到的繼承的使用,若有不對還望指正。
JSONModel的繼承是通常適用于返回的數據中都有一段公共字段,我們有很多類型數據的時候,他們都有id和created_at,那么我們可以創建一個BaseModel
,令所有類型的model直接繼承這個類型model,這樣我們可以避免每個子類重寫。
源碼實現
先來一張流程圖
這里大致講解一下流程:
首先在這個模型類的對象被初始化的時候,遍歷自身到所有的父類,獲取所有的屬性,將其保存到一個字典中去,獲取傳入字典的所有的key,將這些key同保存的屬性進行匹配。若是匹配成功,就進行KVC賦值
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;
這里我們從-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
這種最經典的方法講起:
initWithDictionary
先來看看這個方法的源碼實現:
-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{//檢查參數是否為nilif (!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 succeedif (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
所持有的數據:
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)。
JSONKeyMapper *mapper = [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"propertyOne": @"property_one",@"propertyTwo": @"property_two"
}];
MyModel *model = [[MyModel alloc] initWithDictionary:jsonDict error:nil];
init
我們從第三個方法init開始,看看流程:
-(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是KMapperObjectKeyid 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
方法與模型類關聯起來,以便以后可以方便地訪問。
-(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";//使其變為結構體} //實例化一個scannerscanner = [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 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];}} 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:qpropertyType = 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);}//忽略blockif ([propertyType isEqualToString:@"Block"]) {p = nil;}//如果字典里不存在,則添加到屬性字典里(終于添加上去了。。。)if (p && ![propertyIndex objectForKey:p.name]) {[propertyIndex setValue:p forKey:p.name];}//setter 和 getterif (p){ //name ->NameNSString *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);//再指向自己的父類,知道等于JSONModel才停止class = [class superclass];}//最后保存所有當前類,JSONModel的所有的父類的屬性objc_setAssociatedObject(self.class,&kClassPropertiesKey,[propertyIndex copy],OBJC_ASSOCIATION_RETAIN);//使用關聯對象與模型類關聯起來
}
這里有幾點需要注意:
- 作者使用一個
while
函數,獲取當前類和當前類的除去JSONModel
的所有父類的屬性保存在一個字典中。用來和傳入的字典進行一個映射關系。 - 作者使用
JSONModelClassProperty
類封裝了JSONModel
的每一個屬性,這個類有兩個重要的屬性:一個是name,這是屬性的名稱;另一個是type,這是屬性的類型 - 作者將屬性分為了以下幾個類型:
- 對象(不含有協議)
- 對象(含有協議,即模型嵌套)
- 基本數據類型
- 結構體
__doesDictionary
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{//拿到字典中所有的keyNSArray* incomingKeysArray = [dict allKeys];//返回保存所有屬性名稱的數組(name,age,gender...)NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;//從array中拿到setNSSet* 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 anymoreincomingKeys= nil;requiredProperties= nil;return YES;
}
- 這個方法就將我們使用過程中重寫的
keyMapper
方法中的屬性名稱進行了一個轉換,按照我們的需求進行一個改變 - 這里我們可以看到在最后一個if判斷中說明,我們傳入的字典數據中的key集合不能小于model類的定義的屬性集合,也就是說我們model類中定義的屬性集合必須要賦值。
importDictionary
終于到了將NSDictionary
對象轉化為JSONModel
對象這一步了,來看看會發生什么:
-(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];}//字典不存在對應的keyif (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 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;}//如果是兼容的類型: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 mutableproperty.isMutable||//custom struct propertyproperty.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 namesourceClass]; //source nameSEL selector = NSSelectorFromString(selectorName);//查看自定義的轉換器是否存在BOOL foundCustomTransformer = NO;if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer = YES; } else {//try for hidden custom transformerselectorName = [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對象,并進行一些類型轉換和校驗。
這里有一點筆者之前了解不夠的地方記錄一下:
@property (nonatomic, strong) NSString<Optional> *optionalString;
當我們使用
<Optional>
這個協議標記的時候,說明這個屬性不是必須實現的,當我們沒有實現這個屬性的時候,其被賦值為nil
優點
- Runtime動態解析model數據類型
- keyMapper映射
- KVC賦值
- 使用關聯對象來避免重復解析相同模型,很妙