接上篇MJExtension源碼學(xué)習(xí)(一)
總覽
這一次我們來看MJExtension最新版本的代碼,當(dāng)前最新為3.0.15
在看源碼之前,注意MJExtensionConfig這個(gè)類。因?yàn)樗貙懥?load方法,然后把使用的model的一些配置,統(tǒng)一寫到了這個(gè)文件中。
我大致的看了一下代碼,隨時(shí)版本的更新,改變了一些方法的名字和類名,但是其本質(zhì)的思路是沒有變的,跟最初版本一直。然后再看代碼的過程中會(huì)發(fā)現(xiàn)各種判斷的情況,還有一些看起來不知道做什么用的代碼,其實(shí)沒關(guān)系,因?yàn)殡S著更新這套框架的功能越來越晚上,可以做的事多了,自然為了代碼的健壯性就會(huì)看到很多類型判斷的代碼。
我們直接看到核心代碼這個(gè)方法就可以了
/**
核心代碼:
*/
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
添加了黑名單和白名單的功能,這是獲取到響應(yīng)名單中的成員變量(前提是有設(shè)置)
NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames];
NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames];
往下的mj_enumerateProperties遍歷中,返回了一個(gè)MJProperty類,他的做用是對每個(gè)成員變量做一層封裝,代替了最初的MJIvar這個(gè)類,MJProperty這個(gè)類的內(nèi)部也更加完善了,這個(gè)地方大家可以自己看一下。
再往后就是一些判斷然后復(fù)制,跟之前的差不多,大家看一下就很容易看明白的。
分析封裝MJProperty
這里我想說一下MJProperty中下面的這兩個(gè)方法
/**** 同一個(gè)成員屬性 - 父類和子類的行為可能不一致(originKey、propertyKeys、objectClassInArray) ****/
/** 設(shè)置最原始的key */
- (void)setOriginKey:(id)originKey forClass:(Class)c;
/** 對應(yīng)著字典中的多級key(里面存放的數(shù)組,數(shù)組里面都是MJPropertyKey對象) */
- (NSArray *)propertyKeysForClass:(Class)c;
但從代碼來看很難明確這兩個(gè)方法到底是做什么用的,我用debug跟了一下,發(fā)現(xiàn)其實(shí)這個(gè)propertyKeysForClass返回的數(shù)組中儲(chǔ)存的其實(shí)是當(dāng)前成員變量的一個(gè)層級關(guān)機(jī),我猜測是現(xiàn)在MJExtension可以實(shí)現(xiàn)多層級的映射,這樣記錄下來層級的關(guān)系方便后面的映射取值。
下面我們著重看一下在把成員變量封裝為MJProperty類的對象時(shí),- (void)setOriginKey:(id)originKey forClass:(Class)c;到底做了什么。
/**
* 簡單的字典 -> 模型(key替換,比如ID和id。多級映射,比如 oldName 和 name.oldName)
*/
void keyValues2object4()
{
// 1.定義一個(gè)字典
NSDictionary *dict = @{
@"id" : @"20",
@"desciption" : @"好孩子",
@"name" : @{
@"newName" : @"lufy",
@"oldName" : @"kitty",
@"info" : @[
@"test-data",
@{@"nameChangedTime" : @"2013-08-07"}
]
},
@"other" : @{
@"bag" : @{
@"name" : @"小書包",
@"price" : @100.7
}
}
};
// 2.將字典轉(zhuǎn)為MJStudent模型
MJStudent *stu = [MJStudent mj_objectWithKeyValues:dict];
我們使用這個(gè)例子,MJStudent這個(gè)類中有下列的成員變量
@property (copy, nonatomic) NSString *ID;
@property (copy, nonatomic) NSString *otherName;
@property (copy, nonatomic) NSString *nowName;
@property (copy, nonatomic) NSString *oldName;
@property (copy, nonatomic) NSString *nameChangedTime;
@property (copy, nonatomic) NSString *desc;
@property (strong, nonatomic) MJBag *bag;
@property (strong, nonatomic) NSArray *books;
而且一開始就已經(jīng)說過了,在MJExtensionConfig中已經(jīng)實(shí)現(xiàn)下面的映射關(guān)系
#pragma mark MJStudent中的desc屬性對應(yīng)著字典中的desciption
#pragma mark ....
[MJStudent mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
return @{
@"desc" : @"desciption",
@"oldName" : @"name.oldName",
@"nowName" : @"name.newName",
@"otherName" : @[@"otherName", @"name.newName", @"name.oldName"],
@"nameChangedTime" : @"name.info[1].nameChangedTime",
@"bag" : @"other.bag"
};
}];
// 相當(dāng)于在MJStudent.m中實(shí)現(xiàn)了+(NSDictionary *)mj_replacedKeyFromPropertyName方法
我們就找一個(gè)位置比較深的字段來debug,然后看看她的
[property setOriginKey:[self propertyKey:property.name] forClass:self];到底為他儲(chǔ)存了什么數(shù)據(jù)
我們把斷點(diǎn)先打在block的回調(diào)中
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
}
當(dāng)遍歷到otherName這個(gè)成員變量對應(yīng)的property時(shí),我截下了property中的所有數(shù)據(jù),如下圖

很容易看出來上面的set方法為_propertyKeysDict存進(jìn)去一個(gè)鍵值對,key為model的類名,value是一個(gè)數(shù)組,這是我們只能看到這個(gè)數(shù)組中又有三個(gè)數(shù)組,第一個(gè)數(shù)組有一個(gè)元素,第二個(gè)兩個(gè)元素,第三個(gè)兩個(gè)元素。
單從這里看不出這這個(gè)_propertyKeysDict種到底存的是什么,如果大家debug跟一下整個(gè)set的過程就會(huì)看到,整個(gè)value大數(shù)組有三個(gè)元素,是因?yàn)槲覀冊谟成涮幚淼拇a中@"otherName" : @[@"otherName", @"name.newName", @"name.oldName"],otherName對應(yīng)了一個(gè)三個(gè)元素的數(shù)組,然后從每個(gè)元素中看,因?yàn)?code>name.newName和name.oldName都是做了一級的映射,所以他們下面的數(shù)組會(huì)有兩個(gè)元素,name.newName對應(yīng)的數(shù)組中的兩個(gè)元素分別name對應(yīng)的MJPropertyKey和newName對應(yīng)的MJPropertyKey。
其實(shí)到現(xiàn)在,我們已經(jīng)了解了MJProperty中的成員變量都是為了儲(chǔ)存社會(huì),但是就拿這個(gè)_propertyKeysDict來說,我們還沒有很清楚的明白他為什么要儲(chǔ)存一個(gè)這樣的層級結(jié)構(gòu),所以我們得繼續(xù)往下看后面的取值和賦值。
這個(gè)地方的就很簡單了,其實(shí)還是為了多級映射的取值
// 1.取出屬性值
id value;
NSArray *propertyKeyses = [property propertyKeysForClass:clazz];
for (NSArray *propertyKeys in propertyKeyses) {
value = keyValues;
for (MJPropertyKey *propertyKey in propertyKeys) {
value = [propertyKey valueInObject:value];
}
if (value) break;
}
其中嵌套的for循環(huán),就可以一層層的找到最深的那個(gè)映射,就拿剛才的name.newName來說,以為封裝了兩個(gè)MJPropertyKey,在遍歷第一次的時(shí)候,拿到了name對應(yīng)的數(shù)據(jù),然后在遍歷第二次拿到newName的數(shù)據(jù),可以看出來,每一次的外層的遍歷過程中,如果如果取到value值,遍歷就結(jié)束了, 所以咱們用的例子最終打印的結(jié)果otherName=lufy,,也就是name.newName的值。
個(gè)人感覺這種寫成對應(yīng)數(shù)組的映射,可能是處理有些映射取不到值,哪個(gè)有值用哪個(gè) ~ 如果是單純的多級映射,其實(shí)@"oldName" : @"name.oldName",這種寫法就是ok的。
項(xiàng)目中還有很多其他的細(xì)節(jié)就不單說了。