MJExtension源碼學習(二)

接上篇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

這裏我想說一下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中的全部數據,以下圖
property

很容易看出來上面的set方法爲_propertyKeysDict存進去一個鍵值對,key爲model的類名,value是一個數組,這是咱們只能看到這個數組中又有三個數組,第一個數組有一個元素,第二個兩個元素,第三個兩個元素。

單從這裏看不出這這個_propertyKeysDict種到底存的是什麼,若是你們debug跟一下整個set的過程就會看到,整個value大數組有三個元素,是由於咱們在映射處理的代碼中@"otherName" : @[@"otherName", @"name.newName", @"name.oldName"],otherName對應了一個三個元素的數組,而後從每一個元素中看,由於name.newNamename.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的。

項目中還有不少其餘的細節就不單說了。

相關文章
相關標籤/搜索