MJExtension源碼解讀

MJExtensionjson

A fast, convenient and nonintrusive conversion framework between JSON and model. 轉換速度快、使用簡單方便的字典轉模型框架數組

咱們常常須要從網絡上拉取json數據,而後將json數據轉化爲本身的模型數據,將json數據轉化爲咱們本身的模型數據常用的框架有YYModel和MJExtension,因此如今也是打算花一些時間看一下MJExtension的源碼,而且寫一篇博客記錄一下,由於不記錄下來的話感受很容易忘,學習效果不佳。緩存

###使用MJExtension 1.pod 'MJExtension' 2.#import "MJExtension.h" 3.開始使用bash

最簡單的使用

模型:網絡

//User.h
@interface User : NSObject

@property (nonatomic, copy)NSString *name;
@property (nonatomic, copy)NSString *icon;
@property (nonatomic, assign)unsigned int age;
@property (nonatomic, copy)NSString *height;
@property (nonatomic, strong)NSNumber *money;

@end
複製代碼

字典轉模型:框架

//ViewController.m
NSDictionary *dict = @{
                           @"name" : @"Jack",
                           @"icon" : @"lufy.png",
                           @"age" : @20,
                           @"height" : @"1.55",
                           @"money" : @100.9
                           };
    
    // JSON -> User
    User *user = [User mj_objectWithKeyValues:dict];
    
    NSLog(@"name=%@, icon=%@, age=%u, height=%@, money=%@", user.name, user.icon, user.age, user.height, user.money);
複製代碼

打印結果:源碼分析

name=Jack, icon=lufy.png, age=20, height=1.55, money=100.9
複製代碼

經過一句簡單的代碼,就把字典數據轉化爲了模型數據,很是方便簡潔。性能

#####複雜一點的應用 不少時候json轉模型都不是這樣簡單。有時候會出現模型中嵌套模型或者模型中的屬性名和json數據中的key不一致的狀況。 下面看一下一個Student類的模型:學習

//Student.h
@interface Student : NSObject

@property (nonatomic, copy)NSString *ID;
@property (nonatomic, copy)NSString *desc;
@property (nonatomic, copy)NSString *nowName;
@property (nonatomic, copy)NSString *oldName;
@property (nonatomic, copy)NSString *nameChangedTime;
@property (nonatomic, strong)Bag *bag;

@end
複製代碼

咱們看到Student模型中嵌套着Bag這個模型:優化

//Bag.h
@interface Bag : NSObject

@property (nonatomic, copy)NSString *name;
@property ( nonatomic, assign)double *price;

@end
複製代碼

而後咱們再看一下json數據:

NSDictionary *dict = @{
                           @"id" : @"20",
                           @"description" : @"kids",
                           @"name" : @{
                                   @"newName" : @"lufy",
                                   @"oldName" : @"kitty",
                                   @"info" : @[
                                           @"test-data",
                                           @{
                                               @"nameChangedTime" : @"2013-08"
                                               }
                                           ]
                                   },
                           @"other" : @{
                                   @"bag" : @{
                                           @"name" : @"a red bag",
                                           @"price" : @100.7
                                           }
                                   }
                           };
複製代碼

能夠看到字典數據中是id,而模型中是ID,一樣也有desc和description。模型中有newName和oldName這些屬性,而字典中這些屬性在name字段下面。bag屬性也是同樣的道理,那麼怎麼辦呢? 咱們只須要實現MJExtension中的+ (NSDictionary *)mj_replacedKeyFromPropertyName方法,在Student.m中#import <MJExtension.h>而後實現+ (NSDictionary *)mj_replacedKeyFromPropertyName方法:

//Student.m
+ (NSDictionary *)mj_replacedKeyFromPropertyName
{
    return @{
             @"ID" : @"id",
             @"desc" : @"description",
             @"oldName" : @"name.oldName",
             @"nowName" : @"name.newName",
             @"nameChangedTime" : @"name.info[1].nameChangedTime",
             @"bag" : @"other.bag"
             };
}
複製代碼

這個方法的做用就是在給模型賦值的時候,把右邊字段的值賦給模型中左邊字段的屬性。 轉化一下試試:

// JSON -> Student
    Student *stu = [Student mj_objectWithKeyValues:dict];
    
    // Printing
    NSLog(@"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@",
          stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime);
    // ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08

    NSLog(@"bagName=%@, bagPrice=%d", stu.bag.name, stu.bag.price);
    // bagName=a red bag, bagPrice=100.700000
複製代碼

這個地方須要關注一個地方就是模型中的nameChangedTime這個屬性,在字典中去取值的時候是取name.info[1].nameChangedTime這個字段的值,這個在後面咱們講核心源碼的時候會用到。後面講源碼也會以上面這個爲例子來說,這樣比較好理解。 ###MJExtension核心類簡介 #####MJFoundation

  • 這個類中只有一個方法,就是+ (BOOL)isClassFromFoundation:(Class)c,這個方法用來判斷一個類是不是foundation類及其子類。 #####MJProperty 這個類很是重要,這個類是對咱們類中屬性的再封裝。 首先會經過runtime的方法去遍歷類中的屬性:
unsigned int count;
    objc_property_t *propertyList = class_copyPropertyList([Student class], &count);
    for (int i = 0; i < count; i++) {
        objc_property_t property = propertyList[i];
        const char *propertyName = property_getName(property);
        const char *attris = property_getAttributes(property);
        NSLog(@"%s %s", propertyName, attris);
    }
    
    free(propertyList);
複製代碼

打印結果:

ID T@"NSString",C,N,V_ID
desc T@"NSString",C,N,V_desc
nowName T@"NSString",C,N,V_nowName
oldName T@"NSString",C,N,V_oldName
nameChangedTime T@"NSString",C,N,V_nameChangedTime
bag T@"Bag",&,N,V_bag
複製代碼

經過char類型的attris字符串咱們能夠看到,它中間有一個串是表示它是屬於哪個類的,好比NSString,Bag。

經過遍歷類的屬性,咱們獲得了objc_property_t類型的屬性對象,而後使用這個objc_property_t對象來建立一個對應的MJProperty對象,咱們看看MJ大神是怎麼作的:

#pragma mark - 緩存
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property
{
    MJExtensionSemaphoreCreate
    MJExtensionSemaphoreWait
    MJProperty *propertyObj = objc_getAssociatedObject(self, property);
    if (propertyObj == nil) {
        propertyObj = [[self alloc] init];
        propertyObj.property = property;
        objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    MJExtensionSemaphoreSignal
    return propertyObj;
}
複製代碼

首先MJ大神經過objc_property_t對象這個key去緩存中取,若是緩存中取不到,那麼就根據objc_property_t來建立一個MJProperty對象,而且把這個MJProperty對象經過property這個key與MJProperty類對象關聯起來。那麼下次若是再從緩存中取同一個objc_property_t對應的MJProperty對象就能取到了,就不用再建立了。這也是MJ大神使用緩存的一個地方。 上面代碼塊中propertyObj.property = property;這行代碼觸發了MJProperty對象的set方法:

9B294CD3-D9D5-4C36-ABAA-5EF331EFCA73.png
MJProperty有一個type屬性,這個屬性是MJPropertyType類的,就是表示MJProperty對象的property屬性是屬於什麼類型的。

另外每個MJProperty對象還持有着兩個字典,一個是propertyKeysDict,一個是objectClassInArrayDict

  • propertyKeysDict 這個字典的key是NSStringFromClass(class),值是一個數組,好比在複雜一點的應用中,給模型中的nameChangedTime這個屬性賦值的時候,在字典中去取值的時候要對應name.info[1].nameChangedTime這個字段的值。那麼就要把name,info,1,nameChangedTim,這個四個字段分別封裝爲一個MJPropertyKey,加入一個數組中,做爲value。這個數組在最終取值的時候會用到。
  • objectClassInArrayDict 這個字典的key也是NSStringFromClass(class),值是一個類對象,表示若是這個MJProperty對象的類型是數組,而且數組中的元素類型是模型,那麼這個個字典的value就是模型的類對象。 #####MJPropertyKey 上面說過,給模型中的nameChangedTime這個屬性賦值的時候,在字典中取值的時候要對應name.info[1].nameChangedTime這個字段的值,那麼就要把name,info,1,nameCHangedTime這四個字段分別封裝成一個MJPropertyKey。

它有兩個屬性,一個屬性是name,也就是name,info,1這種,還有一個就是type它是自定義的MJPropertyKeyType類型的枚舉值,這個枚舉值有兩種類型,即MJPropertyKeyTypeDictionaryMJPropertyKeyTypeArray,像name,info這種就屬於MJPropertyKeyTypeDictionary類型的,1就屬於MJPropertyKeyTypeArray類型的。這個也是在取值的時候用的,類型是MJPropertyKeyTypeDictionary就是從字典中取值,類型是MJPropertyKeyTypeArray就是從數組中取值。 #####MJPropertyType MJProperty類有一個屬性是type,這個屬性是MJPropertyType類的,這個type屬性就是表徵這個MJProperty對象它的property屬性屬於什麼類,NSString類或者NSNumber類等等。 MJProperty對象的type是經過截取property的attributes獲得code而後初始化爲MJPropertyType對象獲得的:

_type = [MJPropertyType cachedTypeWithCode:code];
複製代碼

休息一下
###核心源碼分析 咱們就從複雜一點的應用這個例子去看一下MJExtension的核心源碼。 沿着 + (instancetype)mj_objectWithKeyValues:(id)keyValues這個方法一直往下查找就能找到其核心代碼:

- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
{
    // 得到JSON對象
    keyValues = [keyValues mj_JSONObject];
    
    MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues參數不是一個字典");
    
    Class clazz = [self class];
    NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames];
    NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames];
    
    //經過封裝的方法回調一個經過運行時編寫的,用於返回屬性列表的方法。
    [clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
        @try {
            // 0.檢測是否被忽略
            if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return;
            if ([ignoredPropertyNames containsObject:property.name]) return;
            
            // 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;
            }
            
            // 值的過濾
            id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property];
            if (newValue != value) { // 有過濾後的新值
                [property setValue:newValue forObject:self];
                return;
            }
            
            // 若是沒有值,就直接返回
            if (!value || value == [NSNull null]) return;
            
            // 2.複雜處理
            MJPropertyType *type = property.type;
            Class propertyClass = type.typeClass;
            Class objectClass = [property objectClassInArrayForClass:[self class]];//模型數組中對象的類
            
            // 不可變 -> 可變處理
            if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) {
                value = [NSMutableArray arrayWithArray:value];
            } else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) {
                value = [NSMutableDictionary dictionaryWithDictionary:value];
            } else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) {
                value = [NSMutableString stringWithString:value];
            } else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) {
                value = [NSMutableData dataWithData:value];
            }
            
            if (!type.isFromFoundation && propertyClass) { // 模型屬性
                value = [propertyClass mj_objectWithKeyValues:value context:context];
            } else if (objectClass) {
                if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) {
                    // string array -> url array
                    NSMutableArray *urlArray = [NSMutableArray array];
                    for (NSString *string in value) {
                        if (![string isKindOfClass:[NSString class]]) continue;
                        [urlArray addObject:string.mj_url];
                    }
                    value = urlArray;
                } else { // 字典數組-->模型數組
                    value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context];
                }
            } else {
                if (propertyClass == [NSString class]) {
                    if ([value isKindOfClass:[NSNumber class]]) {
                        // NSNumber -> NSString
                        value = [value description];
                    } else if ([value isKindOfClass:[NSURL class]]) {
                        // NSURL -> NSString
                        value = [value absoluteString];
                    }
                } else if ([value isKindOfClass:[NSString class]]) {
                    if (propertyClass == [NSURL class]) {
                        // NSString -> NSURL
                        // 字符串轉碼
                        value = [value mj_url];
                    } else if (type.isNumberType) {
                        NSString *oldValue = value;
                        
                        // NSString -> NSNumber
                        if (type.typeClass == [NSDecimalNumber class]) {
                            value = [NSDecimalNumber decimalNumberWithString:oldValue];
                        } else {
                            value = [numberFormatter_ numberFromString:oldValue];
                        }
                        
                        // 若是是BOOL
                        if (type.isBoolType) {
                            // 字符串轉BOOL(字符串沒有charValue方法)
                            // 系統會調用字符串的charValue轉爲BOOL類型
                            NSString *lower = [oldValue lowercaseString];
                            if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
                                value = @YES;
                            } else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
                                value = @NO;
                            }
                        }
                    }
                }
                
                // value和property類型不匹配
                if (propertyClass && ![value isKindOfClass:propertyClass]) {
                    value = nil;
                }
            }
            
            // 3.賦值
            [property setValue:value forObject:self];
        } @catch (NSException *exception) {
            MJExtensionBuildError([self class], exception.reason);
            MJExtensionLog(@"%@", exception);
        }
    }];
    
    // 轉換完畢
    if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) {
        [self mj_keyValuesDidFinishConvertingToObject];
    }
    return self;
}
複製代碼

這一部分代碼很長,咱們一部分一部分來看: #####1.將json數據轉化爲foundation類型:

// 得到JSON對象
    keyValues = [keyValues mj_JSONObject];
    
    MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues參數不是一個字典");
    
    Class clazz = [self class];
    NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames];
    NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames];
複製代碼

allowedPropertyNames是容許進行字典和模型轉換的屬性名數組,ignoredPropertyNames是不容許進行字典和模型轉換額屬性名數組,這兩個數組通常都是本身在模型類的.m文件中去設置的。 #####2.遍歷整個類的屬性:

+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration
{
    // 得到成員變量
    NSArray *cachedProperties = [self properties];
    
    // 遍歷成員變量
    BOOL stop = NO;
    for (MJProperty *property in cachedProperties) {
        enumeration(property, &stop);
        if (stop) break;
    }
}
複製代碼

再看一下+ (NSMutableArray *)properties方法,其核心部分以下:

[self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) {
                // 1.得到全部的成員變量
   unsigned int outCount = 0;
   objc_property_t *properties = class_copyPropertyList(c, &outCount);
                
     // 2.遍歷每個成員變量
     for (unsigned int i = 0; i<outCount; i++) {
          MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]];
         // 過濾掉Foundation框架類裏面的屬性
         if ([MJFoundation isClassFromFoundation:property.srcClass]) continue;
          property.srcClass = c;
          [property setOriginKey:[self propertyKey:property.name] forClass:self];
          [property setObjectClassInArray:[self propertyObjectClassInArray:property.name] forClass:self];
          [cachedProperties addObject:property];
       }
                
             // 3.釋放內存
      free(properties);
    }];
複製代碼

首先經過+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration這個方法去遍歷當前模型類及其父類,當追溯到Foundation類型的類時就中止遍歷。

有一點須要注意的是,好比有一個Person類,其有兩個屬性name和sex,有一個Student類是繼承自Person類的,這個Student類本身有一個school屬性。那麼當咱們使用runtime的方法讀取Student類的屬性列表時,只能讀取到一個本身聲明的屬性school。可是實際上name和sex也是它的屬性,因此這個時候就要遍歷其父類,拿到全部的屬性。

當咱們拿到模型類的objc_property_t類型的屬性時,就將其封裝成MJProperty對象:

MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]];
複製代碼

+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property方法先嚐試從關聯屬性中經過property對象這個key來取出MJProperty對象,若是取不到就建立一個MJProperty對象,並經過property這個key將其與MJProperty的類對象關聯起來,這樣下次就能夠直接經過關聯屬性來獲得MJProperty的值了:

+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property
{
    MJExtensionSemaphoreCreate
    MJExtensionSemaphoreWait
    MJProperty *propertyObj = objc_getAssociatedObject(self, property);
    if (propertyObj == nil) {
        propertyObj = [[self alloc] init];
        propertyObj.property = property;
        objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    MJExtensionSemaphoreSignal
    return propertyObj;
}
複製代碼

而後再經過propertyObj.property = property;這行代碼觸發set方法,在set方法裏面爲MJProperty對象的name屬性和type屬性賦值,其中type屬性就是和MJProperty對象關聯的property屬於什麼類,是NSNumber類仍是BOOL類等等:

- (void)setProperty:(objc_property_t)property
{
    _property = property;
    
    MJExtensionAssertParamNotNil(property);
    
    // 1.屬性名
    _name = @(property_getName(property));
    
    // 2.成員類型
    NSString *attrs = @(property_getAttributes(property));
    NSUInteger dotLoc = [attrs rangeOfString:@","].location;
    NSString *code = nil;
    NSUInteger loc = 1;
    if (dotLoc == NSNotFound) { // 沒有,
        code = [attrs substringFromIndex:loc];
    } else {
        code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)];
    }
    _type = [MJPropertyType cachedTypeWithCode:code];
}
複製代碼

下面兩行代碼很是重要:

[property setOriginKey:[self propertyKey:property.name] forClass:self];
[property setObjectClassInArray:[self propertyObjectClassInArray:property.name] forClass:self];
複製代碼

對於第一行代碼: + (id)propertyKey:(NSString *)propertyName這個方法是獲取模型的屬性名在字典中對應的key,什麼意思呢?仍是拿第二個例子來講,它有一個nameChangedTime屬性,因爲咱們在模型類中實現了+ (NSDictionary *)mj_replacedKeyFromPropertyName這個方法,且這個方法中與nameChangedTime相對應的是name.info[1].nameChangedTime,因此+ (id)propertyKey:(NSString *)propertyName返回的就是name.info[1].nameChangedTime這個字符串。

對於- (void)setOriginKey:(id)originKey forClass:(Class)c方法,這個方法會把name.info[1].nameChangedTime這個字符串拆解成一段一段,並封裝成一個個MJPropertyKey對象,組成數組,賦值給MJProperty的propertyKeysDict這個字典:

7C9F6395-B72F-425E-BA19-28D8DC66CA67.png
對於第二行代碼 若是模型中有數組類型的屬性,而且數組中的元素也是模型類,那麼就須要在模型類中實現 mj_objectClassInArray方法,就像下面這樣: 模型類中有一個數組類型的屬性statuses,數組中的元素類型是模型,模型類是Status;另外一個數組類型的屬性是ads,數組中的元素類型是模型,模型類是Ad。

+ (NSDictionary *)mj_objectClassInArray
{
    return @{
             @"statuses" : @"Status",
             @"ads" : @"Ad"
             };
}
複製代碼

這時若是在+ (Class)propertyObjectClassInArray:(NSString *)propertyName方法中傳入statuses屬性,那麼返回的就是Status類。

3920D33D-5C12-4645-8F4D-E2E884D2C5A6.png
而後 - (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c方法將這個Status類對象賦值給MJProperty對象的 objectClassInArrayDict字典。

到這裏遍歷類的全部屬性就結束了,這樣得到了整個類的全部屬性,每一個屬性被封裝成了一個MJProperty對象,MJProperty對象有一個property屬性,還有type屬性來表徵這個屬性屬於什麼類。此外MJProperty對象還保存着兩個字典propertyKeysDictobjectClassInArrayDict,這兩個字典的key都是NSStringFromClass(c),前者的value是一個數組,這個數組裏面的元素是MJPropertyKey類型的,主要是用來取值用的,後者的value是一個類對象,若是屬性是一個數組類型的屬性,且數組元素是模型類型,那麼這個值就是模型的類對象。 #####3.對模型進行賦值 首先若是這個屬性不在屬性白名單裏或者在屬性黑名單裏,那麼就返回,不對屬性賦值:

if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return;
if ([ignoredPropertyNames containsObject:property.name]) return;
複製代碼

而後從每一個屬性的propertyKeysDict字典中取出propertyKeys數組,根據propertyKeys數組來取值:

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;
}
複製代碼

咱們看一下- (id)valueInObject:(id)object這個方法是怎麼操做的:

0B08FD70-4C7A-4E0D-AE8C-97ED4C8B201A.png
若是屬性的類型是可變的類型,而取出的value是不可變的類型,那麼就要把不可變類型變換爲可變的類型:

MJPropertyType *type = property.type;
Class propertyClass = type.typeClass;
Class objectClass = [property objectClassInArrayForClass:[self class]];//模型數組中對象的類
            
 // 不可變 -> 可變處理
if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) {
      value = [NSMutableArray arrayWithArray:value];
 } else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) {
       value = [NSMutableDictionary dictionaryWithDictionary:value];
} else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) {
       value = [NSMutableString stringWithString:value];
} else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) {
       value = [NSMutableData dataWithData:value];
            }
複製代碼

上面就是完成了對屬性的第一步賦值,可是這還不夠,若是這個屬性是模型類型,那麼還要對這個模型再進行一次字典轉模型操做。若是這個屬性是數組類型且數組元素是模型類型,那麼還要進行字典數組轉模型數組的操做。或者屬性是NSURL類型,value是NSString類型,這樣也要進行一下轉換:

CFDDC68C-234B-4B63-903F-1B965273EC6C.png
這樣整個模型賦值的過程也就完成了。 ###MJExtension中的一部分緩存操做

MJExtension中進行了大量的緩存操做來優化性能,下面講幾個比較重要的緩存,理解了這些緩存也有助於更深刻的理解整個框架。

####1. NSObject+MJProperty這個分類中保存着一個字典cachedPropertiesDict,這個字典的keyNSStringFromClass(class),值就是一個數組,這個數組裏面存放着一個類的全部屬性。這樣當咱們下一次還要對同一個類進行模型賦值操做,就能夠直接從這個字典裏面取出這個類的一個包含全部屬性的數組了。 ####2. MJProperty這個類中,經過runtime的動態關聯屬性的方法,關聯每個objc_property_t,注意是與類對象相關聯。value是MJProperty對象:

MJProperty *propertyObj = objc_getAssociatedObject(self, property);
    if (propertyObj == nil) {
        propertyObj = [[self alloc] init];
        propertyObj.property = property;
        objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
複製代碼

想象一種狀況,Teacher和Student都繼承自Person,因此Teacher和Student都有Person的屬性,當咱們先給Teacher模型賦值的時候,Person類的每個屬性已經調用了上面的代碼塊封裝成了MJProperty對象,並與MJProperty類對象相關聯。那麼當咱們再給Student模型賦值的時候,也會遍歷Person類的屬性,可是這個時候經過MJProperty *propertyObj = objc_getAssociatedObject(self, property);已經能獲得MJProperty對象了,不用去建立。 ####3. MJPropertyType中有一個types字典,這個字典是在單例中初始化的,types字典的key是code,value是MJPropertyType對象,每次有新的code,就添加到這個字典裏面去,這樣的好處就是若是code一致,就能夠直接從字典中取MJPropertyType。 ####4. 每個MJProperty對象都有一個propertyKeysDict字典,這個字典的key是NSStringFromClass(class),值是一個數組,好比一個MJProperty的名字是name.info[1].text,那麼這個數組就會包括4個MJPropertyKey對象,分別表示name,info,1,text,這些key是在取值的時候用的。那麼問題來了,爲何要設計字典來存儲呢 ,直接用一個數組來存儲不就行了嗎?

其實這個問題和2類似,由於咱們在第二次遍歷Person類中的屬性的時候不用去建立一個MJProperty對象,直接經過關聯屬性去取值就行了,可是Student模型和Teacher模型它們的propertyKeys是有可能不同的,因此這裏須要一個key來加以區分。

因爲我的水平很是有限,這篇博客也只是我本身的理解,所以必定會有理解有誤的地方,還請各位不吝指教。 這篇文章在簡書的地址:MJExtension源碼解讀

相關文章
相關標籤/搜索