iOS開發自定義簡潔實用的高可擴展的Model基類

iOS開發中,網絡請求得到json轉化為字典,然后字典轉化為模型,這是很普遍要做的事。成型的第三方框架也有很多,前段時間比較火的YYKit中的YYMoel對各大這方面的框架包括JsonModel,MjExtension等效率都有所對比。但是授之以魚不如授之以漁,有時候我們僅僅想要的就是字典轉模型而已,簡單,可控,可自定義。今天,小編我提供了一下自己的解決方案。

先來一波思路分析。
后臺返回的數據json(xml很少人在用了吧)的類型的數據格式有對象和數組, 字符串,數字,布爾,null,使用系統自帶的NSJSONSerialization得到字典,會將對象轉化為NSDictionary對象,數組轉化為NSArray對象,字符串轉化為NSString對象,數字和布爾類型轉化為NSValue對象或者子類NSNumber對象,null轉化為NSNull對象。json中的null這個就需要小心了。java的后臺程序可能是直接將model轉化為json,當對象沒初始化為null時,json就會出現null,而不是應該有的{}。null會轉化為NSNull,但是我們認為他是對象類型,轉化為了NSDictionary對象,然后調用了objectForkey,就會報unRecognized selector exception使程序崩潰。關于這點,我曾經和做后臺的同事爭吵過,說:你既然定義json中某key的值是對象類型,為空你也要傳“{}”啊({}會轉化為空字典類型),為什么傳null。他們爭論到:從數據庫中查不到,就沒必要初始化model對象,轉化為json也就會為null。我直接無語了。

直接上代碼吧。注釋還是蠻清晰的,記得不要忘記把那兩個“安全設置”加上,不然,碰到手誤,忘記定義的屬性,程序又該崩潰了。setNilValueForKey:這個不常用,是定義assign類型的屬性給它set nil才會觸發。加上也不多。

- (instancetype)initWithDic:(NSDictionary*)dic
{
    if (!dic || ![dic isKindOfClass:[NSDictionary class]]) {
        return nil;
    }
    
    if (self = [super init]) {
        for (NSString *key in [dic allKeys]) {
            id value = dic[key];
                //1.處理對象類型和數組類型
            if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
                [self setValue:value forKeyPath:key];
            }
                //2.處理空類型:防止出現unRecognized selector exception
            else if ([value isKindOfClass:[NSNull class]]) {
//                [self setValue:nil forKey:key];
            }
                //3.處理其他類型:包括數字,字符串,布爾,全部使用NSString來處理
            else{
                [self setValue:[NSString stringWithFormat:@"%@",value] forKeyPath:key];
            }
            
        }
        
    }
    return self;
}


#pragma mark KVC 安全設置
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
    NSLog(@"%s",__func__);
}
- (void)setNilValueForKey:(NSString *)key
{
    NSLog(@"%s",__func__);
}

JSONModel有一個好處,就是我們在po或者log打印model對象的時候回直接展示他的屬性值。其實就是重寫description方法而已。我們也來一波自定義:

#pragma mark po或者打印時打出內部信息
-(NSString *)description
{
    NSMutableString* text = [NSMutableString stringWithFormat:@"<%@> \n", [self class]];
    NSArray* properties = [self filterPropertys];
    [properties enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSString* key = (NSString*)obj;
        id value = [self valueForKey:key];
        NSString* valueDescription = (value)?[value description]:@"(null)";
        
        if ( ![value respondsToSelector:@selector(count)] && [valueDescription length]>60  ) {
            valueDescription = [NSString stringWithFormat:@"%@...", [valueDescription substringToIndex:59]];
        }
        valueDescription = [valueDescription stringByReplacingOccurrencesOfString:@"\n" withString:@"\n   "];
        [text appendFormat:@"   [%@]: %@\n", key, valueDescription];
    }];
    [text appendFormat:@"</%@>", [self class]];;
    return text;
    
}

方法調用了[self filterPropertys]獲本類的所有屬性。這個用到了所謂的“高大上”的objc的runtime中方法了。先來一波包含頭文件#import <objc/runtime.h>。然后在上代碼。

#pragma mark 獲取一個類的屬性列表
- (NSArray *)filterPropertys
{
    NSMutableArray* props = [NSMutableArray array];
    unsigned int count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    for(int i = 0; i < count; i++){
        objc_property_t property = properties[i];
        const char* char_f =property_getName(property);
        NSString *propertyName = [NSString stringWithUTF8String:char_f];
        [props addObject:propertyName];
            //        NSLog(@"name:%s",property_getName(property));
            //        NSLog(@"attributes:%s",property_getAttributes(property));
    }
    free(properties);
    return props;
}

最后。字典轉模型,你是轉了,那模型轉字典呢?作為一個實用主義的程序員,如果不是有需求用到了,我才不去想這個的,多燒腦子啊??。開發時,有時候需要提交數據給后臺,由于網絡請求的封裝,只需要傳一個字典對象過去就行。如果需要把一個model對象所有屬性都作為參數提交,那么就需要吧model轉化為字典類型。方法如下:

#pragma mark 模型中的字符串類型的屬性轉化為字典
-(NSDictionary*)modelStringPropertiesToDictionary
{
    NSArray* properties = [self filterPropertys];
    NSMutableDictionary* dic = [NSMutableDictionary dictionary];
    [properties enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        
        NSString* key = (NSString*)obj;
        id value = [self valueForKey:key];
        if ([value isKindOfClass:[NSString class]]) {
            NSString* va =  (NSString*)value;
            if (va.length > 0) {
                [dic setObject:value forKey:key];
            }
        }
        
    }];
    return dic;
}

將以上代碼封裝一個BaseModel類,所有model類繼承它。

以上只是根據自己的所學加以運用而已,一千個讀者就有一千個哈姆雷特。好多東西不是沒法解決,只是暫時不知道解決的辦法而已。這篇文章是自己的所學的一個總結,希望對讀者有所幫助。

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

推薦閱讀更多精彩內容