iOS開發·KVC:字典轉模型,防止因本地未定義字段(后臺的字段與本地字符串名不一致)導致數據轉換過程中的奔潰

將后臺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等。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。