將后臺JSON數據中的字典轉成本地的模型,我們一般選用部分優秀的第三方框架,如SBJSON、JSONKit、MJExtension、YYModel等。但是,一些簡單的數據,我們也可以嘗試自己來實現轉換的過程。
更重要的是,有時候在iOS面試的時候,部分面試官會不僅問你某種場景會用到什么框架,更會問你如果要你來實現這個功能,你有沒有解決思路?所以,自己實現字典轉模型還是有必要掌握的。有了這個基礎,在利用運行時runtime的動態特性,你也可以實現這些第三方框架。
筆者的KVC系列為:
1. 建模
假設網絡請求圖片信息并在APP的界面上展示,這里新建一個圖書圖片的模型,id表示圖書的ID,imgUrl是圖書的封面地址(可以用SDWebImage加載該圖),nameStr時圖書的名字,introduceStr是圖書的簡介。
我們建立一個模型如下(暫不先管id
這個含有關鍵字的屬性,后面會講):
- BookModel.h
#import <Foundation/Foundation.h>
@interface BookModel : NSObject
@property (nonatomic,strong) NSString *id;
@property (nonatomic,strong) NSString *imgUrlStr;
@property (nonatomic,strong) NSString *nameStr;
@property (nonatomic,strong) NSString *introduceStr;
+ (instancetype)getBookModelWithDict:(NSDictionary *)dict;
@end
- BookModel.m
#import "BookModel.h"
@implementation BookModel
+ (instancetype)getBookModelWithDict:(NSDictionary *)dict
{
BookModel *bookModel = [[self alloc] init];
[bookModel setValuesForKeysWithDictionary:dict];
return bookModel;
}
@end
當然,你也可以一個一個地為每個屬性分別寫setValue,不嫌麻煩的話
#import "BookModel.h"
@implementation BookModel
+ (instancetype)getBookModelWithDict:(NSDictionary *)dict
{
BookModel *bookModel = [[self alloc] init];
[bookModel setValue:[dict valueForKey:@"id"] forKey:@"id"];
[bookModel setValue:[dict valueForKey:@"imgUrlStr"] forKey:@"imgUrlStr"];
[bookModel setValue:[dict valueForKey:@"nameStr"] forKey:@"nameStr"];
[bookModel setValue:[dict valueForKey:@"introduceStr"] forKey:@"introduceStr"];
return bookModel;
}
@end
2. 含有模型未定義屬性同名字段的字典
字典轉模型過程中也會遇到一些問題,比如,字典里面有多余的keyValue,但是模型沒有定義同名屬性,使用setValuesForKeysWithDictionary
就會崩潰了。
但是,只需要重寫- (void)setValue:(id)value forUndefinedKey:(NSString *)key
方法即可防止未定義的字段與本地字符串名不一致導致的奔潰。
- BookModel.m
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
//空的什么都不寫都可以
}
3. 含有系統關鍵字同名字段的字典
如上所示,許多JSON數據里面會有一個id
的字段, 而id
是iOS的一個關鍵字,不能用關鍵字定義屬性名,此時我們就需要在model類中修改這個屬性的名字,并在- (void)setValue:(id)value forUndefinedKey:(NSString *)key
的方法體中重寫該方法,以針對id
字段作特殊處理。
- BookModel.h
@property (nonatomic,strong) NSString *bookId;
- BookModel.m
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
if([key isEqualToString:@"id"]){
//self.bookId = value;//不推薦
[self setValue:value forKey:@"bookId"]; // 推薦
}
}
4. 示例
假設,APP本地里面用plist寫了一個字典數組,然后寫一個CustomerListModel模型。現在,需要將這個plist字典數組轉換成CustomerListModel模型數組,并在VC取值出來賦給表單元cell的模型數組,用于展示數據。示例的用法參考如下:
- CustomerListModel.m
#import "CustomerListModel.h"
@implementation CustomerListModel
//kvc實現字典轉模型
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
//防止與后臺字段不匹配而造成崩潰
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{}
//類方法:實現字典轉模型,返回模型對象
+ (instancetype)customerListModelWithDict:(NSDictionary *)dict;{
return [[self alloc]initWithDict:dict];
}
//類方法:實現字典轉模型,返回模型對象數組
+ (NSArray<CustomerListModel *> *)customerListModelsWithPlistName:(NSString *)plistName;{
//獲取路徑
NSString *path = [[NSBundle mainBundle]pathForResource:plistName ofType:@"plist"];
//讀取plist
NSArray *dictArr = [NSArray arrayWithContentsOfFile:path];
//字典轉模型
NSMutableArray *modelArr = [NSMutableArray array];
[dictArr enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL * _Nonnull stop) {
[modelArr addObject:[self customerListModelWithDict:dict]];
}];
return modelArr.copy;
}
@end
- 調用處的VC中
- 1).導入模型對象頭文件,并聲明模型對象數組屬性:
@property (nonatomic, strong) NSArray<GloryListModel *> * customerListModelArr;
- 2).重寫懶加載,并在tableView的代理方法調取模型數組用于顯示:
#pragma mark - 懶加載模型數組
- (NSArray< CustomerListModel *> *)customerListModelArr{
if (!_customerListModelArr) {
_customerListModelArr = [CustomerListModel customerListModelsWithPlistName:@"GloryList"];
}
return _customerListModelArr;
}
//cell delegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
GloryListCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([GloryListCell class])];
//定制表格單元分割線
if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
[self.tableView setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
}
cell.customerListModel = self.customerListModelArr[indexPath.row];
return cell;
}
5. 小結
劃重點:
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
方法的作用
- 1、當實現這個方法以后,對未定義的keyValue的處理,防止奔潰。
-(void)setValue:(id)value forUndefinedKey:(NSString *)key{
//空的什么都不寫都可以
//return nil;
}
- 2、如果服務返回的字符串有系統默認不能使用的關鍵字(例如:id ,description等可以進行轉換)
-(void)setValue:(id)value forUndefinedKey:(NSString *)key
{
if ([key isEqualToString:@"id"]) {
//self.id1 = value;// 不推薦
[self setValue:value forKey:@"id1"]; // 推薦
}
if ([key isEqualToString:@"description"]) {
//self.description1 = value;// 不推薦
[self setValue:value forKey:@"description1"]; // 推薦
}
}
- 3、除了自己實現字典轉模型,可以考慮選用部分優秀的第三方框架,如MJExtension、YYModel等。