接上篇MJExtension源碼學習(一)segmentfault
這一次咱們來看MJExtension最新版本的代碼,當前最新爲3.0.15數組
在看源碼以前,注意MJExtensionConfig這個類。由於它重寫了+load方法,而後把使用的model的一些配置,統一寫到了這個文件中。框架
我大體的看了一下代碼,隨時版本的更新,改變了一些方法的名字和類名,可是其本質的思路是沒有變的,跟最第一版本一直。而後再看代碼的過程當中會發現各類判斷的狀況,還有一些看起來不知道作什麼用的代碼,其實不要緊,由於隨着更新這套框架的功能愈來愈晚上,能夠作的事多了,天然爲了代碼的健壯性就會看到不少類型判斷的代碼。學習
咱們直接看到核心代碼這個方法就能夠了atom
/** 核心代碼: */ - (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
添加了黑名單和白名單的功能,這是獲取到響應名單中的成員變量(前提是有設置)spa
NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames]; NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames];
往下的mj_enumerateProperties
遍歷中,返回了一個MJProperty
類,他的作用是對每一個成員變量作一層封裝,代替了最初的MJIvar
這個類,MJProperty
這個類的內部也更加完善了,這個地方你們能夠本身看一下。debug
再日後就是一些判斷而後複製,跟以前的差很少,你們看一下就很容易看明白的。code
這裏我想說一下MJProperty
中下面的這兩個方法對象
/**** 同一個成員屬性 - 父類和子類的行爲可能不一致(originKey、propertyKeys、objectClassInArray) ****/ /** 設置最原始的key */ - (void)setOriginKey:(id)originKey forClass:(Class)c; /** 對應着字典中的多級key(裏面存放的數組,數組裏面都是MJPropertyKey對象) */ - (NSArray *)propertyKeysForClass:(Class)c;
但從代碼來看很難明確這兩個方法究竟是作什麼用的,我用debug跟了一下,發現其實這個propertyKeysForClass
返回的數組中儲存的實際上是當前成員變量的一個層級關機,我猜想是如今MJExtension能夠實現多層級的映射,這樣記錄下來層級的關係方便後面的映射取值。ip
下面咱們着重看一下在把成員變量封裝爲MJProperty
類的對象時,- (void)setOriginKey:(id)originKey forClass:(Class)c;
到底作了什麼。
/** * 簡單的字典 -> 模型(key替換,好比ID和id。多級映射,好比 oldName 和 name.oldName) */ void keyValues2object4() { // 1.定義一個字典 NSDictionary *dict = @{ @"id" : @"20", @"desciption" : @"好孩子", @"name" : @{ @"newName" : @"lufy", @"oldName" : @"kitty", @"info" : @[ @"test-data", @{@"nameChangedTime" : @"2013-08-07"} ] }, @"other" : @{ @"bag" : @{ @"name" : @"小書包", @"price" : @100.7 } } }; // 2.將字典轉爲MJStudent模型 MJStudent *stu = [MJStudent mj_objectWithKeyValues:dict];
咱們使用這個例子,MJStudent這個類中有下列的成員變量
@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;
並且一開始就已經說過了,在MJExtensionConfig中已經實現下面的映射關係
#pragma mark MJStudent中的desc屬性對應着字典中的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" }; }]; // 至關於在MJStudent.m中實現了+(NSDictionary *)mj_replacedKeyFromPropertyName方法
咱們就找一個位置比較深的字段來debug,而後看看她的[property setOriginKey:[self propertyKey:property.name] forClass:self];
到底爲他儲存了什麼數據
咱們把斷點先打在block的回調中
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) { }
當遍歷到otherName
這個成員變量對應的property時,我截下了property中的全部數據,以下圖
很容易看出來上面的set方法爲_propertyKeysDict存進去一個鍵值對,key爲model的類名,value是一個數組,這是咱們只能看到這個數組中又有三個數組,第一個數組有一個元素,第二個兩個元素,第三個兩個元素。
單從這裏看不出這這個_propertyKeysDict種到底存的是什麼,若是你們debug跟一下整個set的過程就會看到,整個value大數組有三個元素,是由於咱們在映射處理的代碼中@"otherName" : @[@"otherName", @"name.newName", @"name.oldName"],
otherName對應了一個三個元素的數組,而後從每一個元素中看,由於name.newName
和name.oldName
都是作了一級的映射,因此他們下面的數組會有兩個元素,name.newName
對應的數組中的兩個元素分別name對應的MJPropertyKey和newName對應的MJPropertyKey。
其實到如今,咱們已經瞭解了MJProperty中的成員變量都是爲了儲存社會,可是就拿這個_propertyKeysDict來講,咱們尚未很清楚的明白他爲何要儲存一個這樣的層級結構,因此咱們得繼續往下看後面的取值和賦值。
這個地方的就很簡單了,其實仍是爲了多級映射的取值
// 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循環,就能夠一層層的找到最深的那個映射,就拿剛纔的name.newName來講,覺得封裝了兩個MJPropertyKey,在遍歷第一次的時候,拿到了name對應的數據,而後在遍歷第二次拿到newName的數據,能夠看出來,每一次的外層的遍歷過程當中,若是若是取到value值,遍歷就結束了, 因此我們用的例子最終打印的結果otherName=lufy,
,也就是name.newName的值。
我的感受這種寫成對應數組的映射,多是處理有些映射取不到值,哪一個有值用哪一個 ~ 若是是單純的多級映射,其實@"oldName" : @"name.oldName",
這種寫法就是ok的。
項目中還有不少其餘的細節就不單說了。