字典轉模型之KVC和MJExtension底層的實現

一 快速生成@property

1 當我們需要用模型去實現一個比較小的功能的時候,由于我們需要對plist文件進行轉化成模型,那么很有可能我們拿到的plist文件中的屬性很多,但是我們又因為只需要研究一個很小的功能,那么我們可以不需要將plist文件中的數據全部轉化為模型數據,我們只需要提取其中的一部分用來實現模型就可以,那么我們怎么做呢?

2 做法:展開某行,然后拷貝該行的item,然后將所有的item折起來,在將拷貝的東西復制到root上即可直接將一整個plist文件改為自己需要處理的文件,用該文件去處理一項特別小的功能.

3 但是由于plist文件中屬性太多,如果我們要創建一個模型,那么不得不寫這么多屬性,首先寫這些屬性都是無用功,沒什么技術含量,那么我們就想,有沒有什么辦法,可以一次性生成寫好的屬性,或者我們直接拷貝就可以了.那么,我們就想到了一個方法,通過一個方法,可以快速的將plist文件中的屬性生成@property的格式,我們只需要將結果拷貝,往@interface中粘貼就可以了.

4 代碼(通過一個分類就可以達到效果,如果需要就直接將分類拷貝到項目中就可以)

//快速生成@property屬性

- (void)creatPropetyCode

{

//創建可變的字符串

NSMutableString*codes = [NSMutableStringstring];

//由于plist文件外層都是字典,那么我們就遍歷字典

[selfenumerateKeysAndObjectsUsingBlock:^(id_Nonnull key,id_Nonnull value,BOOL* _Nonnull stop) {

//初始化變量,用來接收字符串的

NSString*code =nil;

//打印所有的value的類型,就知道BOOL到底是什么類型了.

//2016-03-26 23:06:50.867 03-RunTime(字典轉模型)[853:40171] __NSCFBoolean

NSLog(@"%@",[value class]);

//判斷value的類型,然后響應的憑借出不同類類型的字符串,滿足模型中定義屬性的需求

if([value isKindOfClass:[NSStringclass]]) {

code = [NSStringstringWithFormat:@"@property (nonatomic, strong) NSString *%@;",key];

}elseif([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){

code = [NSStringstringWithFormat:@"@property (nonatomic, strong) BOOL *%@;",key];

}elseif([value isKindOfClass:[NSNumberclass]]){

code = [NSStringstringWithFormat:@"@property (nonatomic, strong) NSNumber *%@;",key];

}elseif([value isKindOfClass:[NSDictionaryclass]]){

code = [NSStringstringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",key];

}elseif([value isKindOfClass:[NSArrayclass]]){

code = [NSStringstringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",key];

}

//拼接字符串

[codes appendFormat:@"\n%@\n",code];

}];

//打印出結果

NSLog(@"%@",codes);

}

//從打印的結果來看,大部分的都能打印出來,但是類型是BOOL的卻打印不出來,我們也沒法通過判斷BOOL的類型來打印,因為BOOL就不是一個類.所以我們必須得想法辦知道BOOL的類型.


二 KVC底層實現

1 模型中的KVC方法

//KVC

[statusItem setValuesForKeysWithDictionary:dict];


2 問題:KVC底層是怎么實現通過字典對模型中的屬性賦值?

2.1 首先我么要明確KVC的使用條件:KVC的使用,必須要保證模型中的屬性名要和字典中的key一一對應,否則使用KVC運行時會報錯的.

2.2 setValuesForKeysWithDictionary:該方法本質其實就是遍歷字典中所有的key,去模型中查找對應的屬性,把值給模型屬性賦值

3 setValuesForKeysWithDictionary:該方法的底層實現原理:

//遍歷

[dict enumerateKeysAndObjectsUsingBlock:^(id_Nonnull key,id_Nonnull obj,BOOL* _Nonnull stop) {

//這行代碼才是真正給模型的屬性賦值

[s setValue:dict[@"source"] forKey:@"source"];

}];


4 遍歷字典的內部給屬性賦值遵循下面幾點條件

/*

[ssetValue:dict[@"source"]forKey:@"source"];

1.首先會去模型中查找有沒有setSource方法,直接調用set方法[ssetSource:dict[@"source"]];

2.去模型中查找有沒有source屬性,source= dict[@"source"]

3.去模型中查找有沒有_source屬性,_source= dict[@"source"]

4.調用對象的setValue:forUndefinedKey:直接報錯

*/

5 很多時候我們不需要plist文件中的所有的屬性都有作用,但是我們又想用kvc來實現給模型屬性賦值,那么我們有什么辦法呢?

5.1 由于我們都知道,如果在plist文件中的屬性和模型中的屬性并不是一一對應的關系,但是如果我們用KVC運行起來會直接報錯,打印出錯誤的結果是:setValue: forUndefinedKey:那么我們是不是想辦法不讓這錯誤提示出來呢.那么我們可以重寫系統的這個方法,里面什么都不需要做.這樣同樣能達到目的.

-(void)setValue:(id)value forUndefinedKey:(NSString *)key

{



三 MJExtension框架的底層實現(一級轉化)

MJ框架與KVC的底層實現不同點:

1 KVC是通過遍歷字典中的所有key,然后去模型中尋找對應的屬性

2 MJ框架是通過先遍歷模型中的屬性,然后去字典中尋找對應的key,所以用MJ框架的時候,模型中的屬性和字典可以不用一一對應,同樣能達到給模型賦值的效果.

3 代碼:

//返回一個創建好的模型

+ (instancetype)modelWithDict:(NSDictionary*)dict

{

//創建一個模型

idobjc = [[selfalloc] init];

intcount =0;

/*

方法:獲取成員變量列表

參數一:class獲取哪個類成員變量列表

參數二:count成員變量總數

*/

//成員變量數組指向數組第0個元素

Ivar *ivarList = class_copyIvarList(self, &count);

//遍歷所有成員變量

for(inti =0; i < count; i++) {

//獲取成員變量

Ivar ivar = ivarList[i];

//獲取成員變量名稱(將c轉為oc)

NSString*ivarName = [NSStringstringWithUTF8String:ivar_getName(ivar)];

//成員變量名稱轉換key(將成員變量前邊的"_"截取掉)

NSString*key = [ivarName substringFromIndex:1];

//從字典中取出對應value

idvalue = dict[key];

//給模型中屬性賦值(底層會去找對應的屬性和值)

[objc setValue:value forKey:key];

}

returnobjc;

}

四 附加知識點

1 const與宏的區別

1.1 編譯的時刻不一樣: 宏:預編譯 const:編譯

1.2 編譯檢查:宏不會做編譯檢查 const會

1.3 宏的好處:宏可以定義函數和方法 const不可以

1.4 宏壞處:大量使用宏,會導致預編譯時間過長

2 const作用

2.1 用于修飾右邊變量(基本變量,指針變量)

2.2 被const修飾變量只讀

2.3 有關const的面試題

int*constp1;// p1:只讀*p1:變量

constint*p2;// p2:變量*p2:只讀

intconst*p3;// p3:變量*p3:只讀

intconst*constp4;// p4:只讀*p4:只讀

constint*constp5;// p5:只讀*p5:只讀


3 const在開發當中的使用

3.1 const替換宏,宏:常用字符串或者基本數據定義成宏 -> const

3.2 修飾方法參數,讓方法參數只讀

4 static和extern

4.1 static:

—–>1> 修飾局部變量,只要被static修飾局部變量,這個局部變量的聲明周期就會延長(整個app運行過程中都在),作用域不變

—–>2> 修飾全局變量,只要被static修飾全局變量,這個全局變量只能在當前文件下使用

—–>3> 分配時刻:程序一運行的時候就會分配內存

4.2 extern:

—–>1> 僅僅只用來聲明外部全局變量

—–>2> extern不能用來定義變量

5 static和const聯合使用

5.1 被static修飾全局變量,全局變量只能在當前文件下使用

5.2 被const修飾,只讀.

6 開發中是如何避免重復定義?

做法:只要定義全局變量,都不能再自己的類中定義,一般開發中,我們會搞一個公共的文件來定義全局變量

五 總結

以上就是今天我給大家分享的知識點,很多知識點都寫得很詳細了,如果有不懂的可以給我留言,謝謝.我明天還會持續更新新的知識點.記得關注哦!!!

Baidu Button BEGIN

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

推薦閱讀更多精彩內容