一、簡(jiǎn)單介紹setValuesForKeysWithDictionary
在iOS的日常開(kāi)發(fā)中,經(jīng)常用到model來(lái)明確所使用的數(shù)據(jù)模型,這樣子可視化、數(shù)據(jù)邏輯等都明顯高于字典。之前公司舊項(xiàng)目沒(méi)有使用model來(lái)模型化字典,所有的數(shù)據(jù)都是在VC中用字典來(lái)存儲(chǔ)(一個(gè)人負(fù)責(zé)的,估計(jì)是為了省事。。。可實(shí)際上感覺(jué)費(fèi)了更多的代碼和時(shí)間,同時(shí)這個(gè)是一個(gè)靜態(tài)庫(kù)項(xiàng)目,我的天,要看一個(gè)數(shù)據(jù)模型就要跑項(xiàng)目打印數(shù)據(jù),到我這里就費(fèi)勁)。
所以,將數(shù)據(jù)模型化,無(wú)論在MVC、MVVM還是我公司最近用的MVP架構(gòu)中都是十分必要的,不僅僅提供方便了其他人快速了解字典內(nèi)的數(shù)據(jù),同時(shí)也可以將數(shù)據(jù)與頁(yè)面分離。
在字典模型化下,setValuesForKeysWithDictionary簡(jiǎn)直不要太帥,沒(méi)有使用其的情況下,我們對(duì)于字典模型化,是一條條的賦值,比如:
NSMutableDictionary * dic = [NSMutableDictionary new];
[dic setObject:@"a" forKey:@"name"];
[dic setObject:@"mail" forKey:@"sex"];
AModel *model = [AModel new];
model.name = dic[@"name"];
model.sex = dic[@"sex"];
這個(gè)樣子,假如數(shù)據(jù)量大的話(huà),不僅代碼量大,為了一個(gè)這么沒(méi)有技術(shù)含量的工作浪費(fèi)大量的時(shí)間也是沒(méi)有必要的。所以,蘋(píng)果提供了setValuesForKeysWithDictionary來(lái)解決這種問(wèn)題。上面的例子,可以這樣來(lái)實(shí)現(xiàn)。
NSMutableDictionary * dic = [NSMutableDictionary new];
[dic setObject:@"a" forKey:@"name"];
[dic setObject:@"mail" forKey:@"sex"];
AModel * model = [AModel new];
[model setValuesForKeysWithDictionary:dic];
因?yàn)樵陧?xiàng)目中使用,有多個(gè)model的情況,可以創(chuàng)建一個(gè)基類(lèi)BaseModel來(lái)管理,所有的繼承這個(gè)類(lèi),將setValuesForKeysWithDictionary放入init方法中。
#import "BaseModel.h"
@implementation BaseModel
// kvc 賦值
- (instancetype)initValueWithDictionary:(NSDictionary *)dic{
self = [super init];
if (self) {
[self setValuesForKeysWithDictionary:dic];
}
return self;
}
@end
setValuesForKeysWithDictionary非常好用,不需要你來(lái)一一的給對(duì)象賦值而直接從字典初始化即可,注意的是模型與字典的key值必須對(duì)應(yīng)。用的不好會(huì)經(jīng)常崩潰!我們可以來(lái)看一下兩種情況:
1、字典中無(wú)而model中有的數(shù)據(jù)
//model 有name sex mobile
NSMutableDictionary * dic = [NSMutableDictionary new];
[dic setObject:@"a" forKey:@"name"];
[dic setObject:@"mail" forKey:@"sex"];
AModel *model = [[AModel alloc] initValueWithDictionary:dic];
正常運(yùn)行,model中的mobile打印出來(lái)是 null。
2、字典中有model中沒(méi)有的數(shù)據(jù)
//model 只有name sex
NSMutableDictionary * dic = [NSMutableDictionary new];
[dic setObject:@"a" forKey:@"name"];
[dic setObject:@"mail" forKey:@"sex"];
[dic setObject:@"廈門(mén)" forKey:@"address"];
AModel *model = [[AModel alloc] initValueWithDictionary:dic];
這種情況會(huì)崩潰,錯(cuò)誤
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<AModel 0x17403e280> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key address.'
這種情況是因?yàn)椋瑂etValuesForKeysWithDictionary在模型中找不到對(duì)應(yīng)的key--address 來(lái)進(jìn)行賦值處理,所以出錯(cuò)崩潰。
這種情況其實(shí)很好解決,利用:
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
在model.m中實(shí)現(xiàn)這個(gè)方法,處理key值不存在或者是沒(méi)有對(duì)應(yīng)的情況。
//不做任何處理,即key值不存在的情況下
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
}
//model.m中
//key值沒(méi)有對(duì)應(yīng)的情況下
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
if ([key isEqualToString:@"UserName"]) {
self.name = value;
}
}
//VC中的賦值
//model 有name sex mobile
NSMutableDictionary * dic = [NSMutableDictionary new];
[dic setObject:@"a" forKey:@"UserName"];
[dic setObject:@"mail" forKey:@"sex"];
[dic setObject:@"廈門(mén)" forKey:@"address"];
AModel *model = [[AModel alloc] initValueWithDictionary:dic];
這樣子就不會(huì)出現(xiàn)了崩潰的現(xiàn)象。
二、potocol管理子類(lèi)的特殊情況。
在項(xiàng)目中model可以設(shè)立一個(gè)基類(lèi),所有的model都繼承這個(gè)類(lèi),就像我上面所說(shuō)的,因?yàn)榇蟛糠值膍odel都有這兩種行為:初始化和特殊key值處理。在基類(lèi)與子類(lèi)的關(guān)系中,我喜歡用potocol來(lái)管理差異(MVP)(個(gè)人理解:相比于子類(lèi)重寫(xiě)基類(lèi)的方法,應(yīng)該是層次更清楚,讓其他人直接明白子類(lèi)需要的實(shí)現(xiàn)的協(xié)議)。
BaseModel.h
#import <Foundation/Foundation.h>
@protocol ModelProtocol <NSObject>
@optional
- (instancetype)initValueWithDictionary:(NSDictionary *)dic;
- (void)initValue:(id)value forUndefinedKey:(NSString *)key;
@end
@interface BaseModel : NSObject<ModelProtocol>
@end
BaseModel.m
#import "BaseModel.h"
@implementation BaseModel
// kvc 賦值
- (instancetype)initValueWithDictionary:(NSDictionary *)dic{
self = [super init];
if (self) {
[self setValuesForKeysWithDictionary:dic];
}
return self;
}
//沒(méi)有找到模型key
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
if ([self respondsToSelector:@selector(initValue:forUndefinedKey:)]) {
[self initValue:value forUndefinedKey:key];
}
}
在子類(lèi)中,實(shí)現(xiàn)特殊key值
- (void)initValue:(id)value forUndefinedKey:(NSString *)key{
if ([key isEqualToString:@"results"]) {
}
}
三、子類(lèi)模型屬性是一個(gè)類(lèi)型為其他模型的數(shù)組。
model中有數(shù)組,數(shù)組內(nèi)類(lèi)型也是模型的話(huà),也可以利用:
- (void)setValue:(id)value forUndefinedKey:(NSString *)key;
來(lái)實(shí)現(xiàn)。
AModel.h
#import "BaseModel.h"
@class BModel;
@interface AModel : BaseModel
@property (nonatomic, strong) NSString * name;
@property (nonatomic, strong) NSString * sex;
@property (nonatomic, strong) NSString * mobile;
@property (nonatomic, strong) NSMutableArray<BModel *>* bArray;
@end
AModel.m
#import "AModel.h"
#import "BModel.h"
@implementation AModel
- (void)initValue:(id)value forUndefinedKey:(NSString *)key{
if ([key isEqualToString:@"array"]) {
if ([value isKindOfClass:[NSArray class]]) {
for (NSDictionary *dic in value) {
[self.bArray addObject:[[BModel alloc] initValueWithDictionary:dic]];
}
}
}
}
//懶加載
-(NSMutableArray<BModel *> *)bArray{
if (_bArray == nil) {
_bArray = [NSMutableArray<BModel *> new];
}
return _bArray;
}
@end