天天看點

JSONModel的源碼解析JSONModel的源碼解析

JSONModel的源碼解析

一、JSONModel和YYModel的比較

1、json中有[NSNull null]類型 — JSONModel和YYModel都有适配

2、異常情況:NSString <-> NSNumber — JSONModel和YYModel都有适配

3、異常情況:NSString <-> NSUInteger —JSONModel沒有适配,會crash,YYModel有适配

4、異常情況:NSArray <-> NSString —JSONModel和YYModel都有沒有适配

5、NSCoding 協定(持久化)的支援 —JSONModel和YYModel都有适配

6、是否可以嵌套Model —JSONModel和YYModel都有适配

7、NSArray中可以包含Model —JSONModel和YYModel都有适配

8、未知字段的适配(向後相容) —JSONModel和YYModel都有适配

9、繼承情況下對于多态的支援 —JSONModel沒有适配,YYModel适配了

注意:好像YYModel再gethub上面的更新時間停留在2年前,好像近期都沒有更新了。

二、JSONModel的源碼解析

我們可以先看看核心代碼

1、先是判斷傳入的字典是否為空,如果為空傳回為空的錯誤

2、再判斷傳入的資料是否是字典類型,如果不是字典類型不正确的錯誤

3、核心的代碼,通過init方法初始化映射property,(核心代碼,等會再下面解釋)

4、擷取目前類的keyMapper

5、檢查映射結構是否能從我們傳入的dict中找到對應的資料,如果不能找到,就傳回nil,并且抛出錯誤

6、根據傳入的dict進行資料的指派,如果指派沒有成功,就傳回nil,并且抛出錯誤

7、根據本地的錯誤來判斷是否有錯誤,如果有錯誤,就傳回nil

8、傳回self

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{
    //check for nil input
  // 第一步: 先是判斷傳入的字典是否為空,如果為空傳回為空的錯誤
    if (!dict) {
        if (err) *err = [JSONModelError errorInputIsNil];
        return nil;
    }

    //invalid input, just create empty instance
    //第二步:再判斷傳入的資料是否是字典類型,如果不是字典類型不正确的錯誤
    if (![dict isKindOfClass:[NSDictionary class]]) {
        if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
        return nil;
    }

    //create a class instance
    //第三步:核心的代碼,通過init方法初始化映射property
    self = [self init];
    if (!self) {

        //super init didn't succeed
        if (err) *err = [JSONModelError errorModelIsInvalid];
        return nil;
    }
    //第四步:擷取目前類的keyMapper
    //key mapping
    JSONKeyMapper* keyMapper = [self __keyMapper];
    //第五步:檢查映射結構是否能從我們傳入的dict中找到對應的資料,如果不能找到,就傳回nil,并且抛出錯誤
    //check incoming data structure
    if (![self __doesDictionary:dict matchModelWithKeyMapper:keyMapper error:err]) {
        return nil;
    }

    //import the data from a dictionary
    //第六步:根據傳入的dict進行資料的指派,如果指派沒有成功,就傳回nil,并且抛出錯誤。
    if (![self __importDictionary:dict withKeyMapper:keyMapper validation:YES error:err]) {
        return nil;
    }
    //第七步:根據本地的錯誤來判斷是否有錯誤,如果有錯誤,就傳回nil,并且抛出錯誤。
    //run any custom model validation
    if (![self validate:err]) {
        return nil;
    }
    //第八步:傳回self
    //model is valid! yay!
    return self;
}
           

第三步:核心代碼的解析–通過init方法初始化映射property。

a、他會先調用init方法,init方法中點用了“setup“方法。–[self setup]。

-(id)init
{
    self = [super init];
    if (self) {
        //do initial class setup
        [self __setup_];
    }
    return self;
}
           

我們先解析一下“setup”方法

1、先是通過AssociateObject來判斷是否進行過映射property的緩存,如果沒有就使用“__inspectProperties”方法進行映射property的緩存–(後面我會解析一下“__inspectProperties”方法,介紹怎麼進行映射property的緩存)

2、擷取目前類的keyMapper映射。

3、判斷一下,目前的keyMapper是否存在和是否進行映射過,如果沒有進行映射就使用AssociateObject方法進行映射。

4、進行AssociateObject映射。

-(void)__setup__
{
    //if first instance of this model, generate the property list
   //第一步: 先是通過AssociateObject來判斷是否進行過映射property的緩存,如果沒有就使用“__inspectProperties”方法進行映射property的緩存
    if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
    //進行映射property的緩存
        [self __inspectProperties];
    }

    //if there's a custom key mapper, store it in the associated object
    //第二步:擷取keyMapper
    id mapper = [[self class] keyMapper];
    //第三步:判斷一下,目前的keyMapper是否存在和是否進行映射過,如果沒有進行映射就使用AssociateObject方法進行映射
    if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
    //第四步:進行AssociateObject映射
        objc_setAssociatedObject(
                                 self.class,
                                 &kMapperObjectKey,
                                 mapper,
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }
}
           

解析一下“__inspectProperties”方法,介紹怎麼進行映射property的緩存

注意:NSScanner是用于在字元串中掃描指定的字元,特别是把它們翻譯/轉換為數字和别的字元串。可以在建立NSScaner時指定它的string屬性,然後scanner會按照你的要求從頭到尾地掃描這個字元串的每個字元。

1、先是擷取目前class的property清單和個數

2、然後再周遊這些property

3、把我們的property通過一個局部變量進行指派–JSONModelClassProperty,這個是JSONModel提供的類,來解析每個property。

4、擷取property的名稱給目前這個局部變量

5、擷取這個property的屬性

6、判斷這個property的屬性值裡面是否包含”Tc,”,如果包含就設定structName為BOOL

7、掃描property屬性

8、設定property的類型

9、判斷并設定property的是否是可變的

10、判斷property的是否我們允許的json類型

11、解析protocol的string

12、檢查property是否為structure

13、判斷property是不是Optional

14、判斷property是不是Ignored

15、判斷property是不是隻讀屬性

16、通過kvc去設定相應的值

17、使用AssociateObject進行緩存

-(void)__inspectProperties
{
    //JMLog(@"Inspect class: %@", [self class]);

    NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];

    //temp variables for the loops
    Class class = [self class];
    NSScanner* scanner = nil;
    NSString* propertyType = nil;

    // inspect inherited properties up to the JSONModel class
    while (class != [JSONModel class]) {
        //JMLog(@"inspecting: %@", NSStringFromClass(class));
       //第一步:先是擷取目前class的property清單和個數
        unsigned int propertyCount;
        objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
        //第二步:周遊property
        //loop over the class properties
        for (unsigned int i = ; i < propertyCount; i++) {
//第三步:建立一個解析和判斷每個property的局部變量JSONModelClassProperty
            JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];

            //get property name
            objc_property_t property = properties[i];
            const char *propertyName = property_getName(property);
            //第四步:擷取property的名稱給目前這個局部變量
            p.name = @(propertyName);

            //JMLog(@"property: %@", p.name);

            //get property attributes
            //第五步:擷取這個property的屬性
            const char *attrs = property_getAttributes(property);
            NSString* propertyAttributes = @(attrs);
            //第六步:判斷這個property的屬性值裡面是否包含"Tc,",如果包含就設定structName為BOOL
            if ([propertyAttributes hasPrefix:@"Tc,"]) {
                //mask BOOLs as structs so they can have custom convertors
                p.structName = @"BOOL";
            }
            //第七步:掃描property屬性
            scanner = [NSScanner scannerWithString: propertyAttributes];

            //JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
            [scanner scanUpToString:@"T" intoString: nil];
            [scanner scanString:@"T" intoString:nil];

            //check if the property is an instance of a class
            //解析一個類,檢查屬性是否為類的執行個體
            if ([scanner scanString:@"@\"" intoString: &propertyType]) {

                [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
                                        intoString:&propertyType];

                //JMLog(@"type: %@", propertyClassName);
                //第八步:設定property的類型
                p.type = NSClassFromString(propertyType);
                //第九步:判斷并設定property的是否是可變的
                p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);
                //第十步:判斷property的是否我們允許的json類型
                p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];

                //read through the property protocols

            //第十一步:解析protocol的string
                while ([scanner scanString:@"<" intoString:NULL]) {

                    NSString* protocolName = nil;

                    [scanner scanUpToString:@">" intoString: &protocolName];
                    if ([protocolName isEqualToString:@"Optional"]) {
                        p.isOptional = YES;
                    } else if([protocolName isEqualToString:@"Index"]) {
                        p.isIndex = YES;
                        objc_setAssociatedObject(
                                                 self.class,
                                                 &kIndexPropertyNameKey,
                                                 p.name,
                                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                                 );
                    } else if([protocolName isEqualToString:@"ConvertOnDemand"]) {
                        p.convertsOnDemand = YES;
                    } else if([protocolName isEqualToString:@"Ignore"]) {
                        p = nil;
                    } else if ([self propertyIsReadOnly:p.name]) {
                        p = nil;
                    } else {
                        p.protocol = protocolName;
                    }

                    [scanner scanString:@">" intoString:NULL];
                }

            }
            //check if the property is a structure
            //第十二步:檢查property是否為structure
            else if ([scanner scanString:@"{" intoString: &propertyType]) {
                [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
                                    intoString:&propertyType];

                p.isStandardJSONType = NO;
                p.structName = propertyType;

            }
            //the property must be a primitive
            else {

                //the property contains a primitive data type
                [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
                                        intoString:&propertyType];

                //get the full name of the primitive type
                propertyType = valueTransformer.primitivesNames[propertyType];

                if (![allowedPrimitiveTypes containsObject:propertyType]) {

                    //type not allowed - programmer mistaked -> exception
                    @throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"
                                                   reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]
                                                 userInfo:nil];
                }

            }

            NSString *nsPropertyName = @(propertyName);
            //第十三步:判斷property是不是Optional
            if([[self class] propertyIsOptional:nsPropertyName]){
                p.isOptional = YES;
            }
                        //第十四步:判斷property是不是Ignored
            if([[self class] propertyIsIgnored:nsPropertyName]){
                p = nil;
            }
                                    //第十五步:判斷property是不是隻讀屬性

            if([self propertyIsReadOnly:nsPropertyName]) {
                p = nil;
            }

            //add the property object to the temp index
            //第十六步:通過kvc去設定相應的值
            if (p) {
                [propertyIndex setValue:p forKey:p.name];
            }
        }

        free(properties);

        //ascend to the super of the class
        //(will do that until it reaches the root class - JSONModel)
        class = [class superclass];
    }

    //finally store the property index in the static property index
    //第十七步:使用AssociateObject進行緩存
    objc_setAssociatedObject(
                             self.class,
                             &kClassPropertiesKey,
                             [propertyIndex copy],
                             OBJC_ASSOCIATION_RETAIN // This is atomic
                             );
}
           

在解析一下“-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err”查值的代碼。

1、檢查一下所有的必要屬性都存在,并且把他們都放入set中

2、判斷是否存在keyMapper映射,如果存在,在對應的set中找到對應的key進行替換

3、判斷那些必須的Propertie的集合是否全部都包括

-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{
    //check if all required properties are present
    //第一步:檢查一下所有的必要屬性都存在,并且把他們都放入set中
    NSArray* incomingKeysArray = [dict allKeys];
    NSMutableSet* requiredProperties = [self __requiredPropertyNames];
    NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];

    //transform the key names, if neccessary
    //第二步:判斷是否存在keyMapper映射,如果存在,在對應的set中找到對應的key進行替換
    if (keyMapper) {

        NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
        NSString* transformedName = nil;

        //loop over the required properties list
        //這個是周遊所需要的屬性清單
        for (JSONModelClassProperty* property in [self __properties__]) {

            //get the mapped key path
            transformedName = 
            //擷取mapped的關鍵值transformedName
            keyMapper.modelToJSONKeyBlock(property.name);

            //chek if exists and if so, add to incoming keys
            //檢查該值是否存在,如果存在,加入到incoming集合中
            id value;
            @try {
                value = [dict valueForKeyPath:transformedName];
            }
            @catch (NSException *exception) {
                value = dict[transformedName];
            }

            if (value) {
                [transformedIncomingKeys addObject: property.name];
            }
        }

        //overwrite the raw incoming list with the mapped key names
        incomingKeys = transformedIncomingKeys;
    }

    //check for missing input keys
    //第三步:判斷那些必須的Properties的集合是否全部都包括
    if (![requiredProperties isSubsetOfSet:incomingKeys]) {

        //get a list of the missing properties
        [requiredProperties minusSet:incomingKeys];

        //not all required properties are in - invalid input
        JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self.class, requiredProperties);

        if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];
        return NO;
    }

    //not needed anymore
    incomingKeys= nil;
    requiredProperties= nil;

    return YES;
}
           

下面這個方法是判斷哪些屬性是必須的

-(NSMutableSet*)__requiredPropertyNames
{
    //fetch the associated property names
    NSMutableSet* classRequiredPropertyNames = objc_getAssociatedObject(self.class, &kClassRequiredPropertyNamesKey);

    if (!classRequiredPropertyNames) {
        classRequiredPropertyNames = [NSMutableSet set];
        [[self __properties__] enumerateObjectsUsingBlock:^(JSONModelClassProperty* p, NSUInteger idx, BOOL *stop) {
            if (!p.isOptional) [classRequiredPropertyNames addObject:p.name];
        }];

        //persist the list
        objc_setAssociatedObject(
                                 self.class,
                                 &kClassRequiredPropertyNamesKey,
                                 classRequiredPropertyNames,
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }
    return classRequiredPropertyNames;
}
           

最後解析一下查找屬性以後的指派操作

1、循環周遊映射出來的JSONModelClassProperty

2、擷取keyMapper的映射,擷取真正的值

3、判斷資料輸入的類型是不是我們允許的json類型

4、檢查model中是否有比對的屬性

5、檢查自定義的setter,使用對應的set進行指派—-(這個具體看代碼裡面的判斷,我就不列舉出來了)

6、處理一些轉化類型,比如string <-> number、string <-> url

-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{
    //loop over the incoming keys and set self's properties
    //第一步:循環周遊映射出來的JSONModelClassProperty
    for (JSONModelClassProperty* property in [self __properties__]) {

        //convert key name ot model keys, if a mapper is provided
        //第二步:擷取keyMapper的映射,擷取真正的值
        NSString* jsonKeyPath = property.name;
        if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name );

        //JMLog(@"keyPath: %@", jsonKeyPath);

        //general check for data type compliance
        id jsonValue;
        @try {
            jsonValue = [dict valueForKeyPath: jsonKeyPath];
        }
        @catch (NSException *exception) {
            jsonValue = dict[jsonKeyPath];
        }

        //check for Optional properties
        //判斷屬性是否為空,在判斷屬性是否是可選的
        if (isNull(jsonValue)) {
            //skip this property, continue with next property
            if (property.isOptional || !validation) continue;

            if (err) {
                //null value for required property
                NSString* msg = [NSString stringWithFormat:@"Value of required model key %@ is null", property.name];
                JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                *err = [dataErr errorByPrependingKeyPathComponent:property.name];
            }
            return NO;
        }

        Class jsonValueClass = [jsonValue class];
        BOOL isValueOfAllowedType = NO;
        //第三步:判斷資料輸入的類型是不是我們允許的json類型
        for (Class allowedType in allowedJSONTypes) {
            if ( [jsonValueClass isSubclassOfClass: allowedType] ) {
                isValueOfAllowedType = YES;
                break;
            }
        }

        if (isValueOfAllowedType==NO) {
            //type not allowed
            JMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass));

            if (err) {
                NSString* msg = [NSString stringWithFormat:@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)];
                JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                *err = [dataErr errorByPrependingKeyPathComponent:property.name];
            }
            return NO;
        }

        //check if there's matching property in the model
        //第四步:檢查model中是否有比對的屬性
        if (property) {

            // check for custom setter, than the model doesn't need to do any guessing
            // how to read the property's value from JSON
            //第五步:檢查自定義的setter,使用對應的set進行指派
            if ([self __customSetValue:jsonValue forProperty:property]) {
                //skip to next JSON key
                continue;
            };

            // 0) handle primitives
            //如果是基礎類型,不如Bool等,我們直接使用kvc進行指派
            if (property.type == nil && property.structName==nil) {

                //generic setter
                [self setValue:jsonValue forKey: property.name];

                //skip directly to the next key
                continue;
            }

            // 0.5) handle nils
            if (isNull(jsonValue)) {
                [self setValue:nil forKey: property.name];
                continue;
            }


            // 1) check if property is itself a JSONModel
            //檢查屬性是不是JSONModel結構,進行周遊,把所有的結構周遊并且指派
            if ([self __isJSONModelSubClass:property.type]) {

                //initialize the property's model, store it
                JSONModelError* initErr = nil;
                id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];

                if (!value) {
                    //skip this property, continue with next property
                    if (property.isOptional || !validation) continue;

                    // Propagate the error, including the property name as the key-path component
                    if((err != nil) && (initErr != nil))
                    {
                        *err = [initErr errorByPrependingKeyPathComponent:property.name];
                    }
                    return NO;
                }
                [self setValue:value forKey: property.name];

                //for clarity, does the same without continue
                continue;

            } else {

                // 2) check if there's a protocol to the property
                //  ) might or not be the case there's a built in transofrm for it
                // 判斷property中是否包含protocol的字段。(該字段主要用來表明array或者dictionary中的對象類型)

                if (property.protocol) {

                    //JMLog(@"proto: %@", p.protocol);
                    //循環周遊子内容,再将對應的類型指派給對應的dictionary或者array
                    jsonValue = [self __transform:jsonValue forProperty:property error:err];
                    if (!jsonValue) {
                        if ((err != nil) && (*err == nil)) {
                            NSString* msg = [NSString stringWithFormat:@"Failed to transform value, but no error was set during transformation. (%@)", property];
                            JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];
                            *err = [dataErr errorByPrependingKeyPathComponent:property.name];
                        }
                        return NO;
                    }
                }

                // 3.1) handle matching standard JSON types
                //判斷是否是标準的json類型
                if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {

                    //mutable properties
                    //判斷是否可變的類型
                    if (property.isMutable) {
                        jsonValue = [jsonValue mutableCopy];
                    }

                    //set the property value
                    //kvc指派
                    [self setValue:jsonValue forKey: property.name];
                    continue;
                }

                // 3.3) handle values to transform
                //第六步:處理一些轉化類型,比如string <-> number、string <-> url
                //做一些基礎的判斷,是否是對應的類型,是否為空,是否是可變類型,是否是自定義的結構
                if (
                    (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
                    ||
                    //the property is mutable
                    property.isMutable
                    ||
                    //custom struct property
                    property.structName
                    ) {

                    // searched around the web how to do this better
                    // but did not find any solution, maybe that's the best idea? (hardly)
                    //下面這句,就是擷取真實的json資料的類型
                    Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];

                    //JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);

                    //build a method selector for the property and json object classes
                    //為屬性和json對象類建構一個方法選擇器
                    NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",
                                              (property.structName? property.structName : property.type), //target name
                                              sourceClass]; //source name
                    SEL selector = NSSelectorFromString(selectorName);

                    //check for custom transformer
//檢查自定義的轉換方法
                    BOOL foundCustomTransformer = NO;
                    if ([valueTransformer respondsToSelector:selector]) {
                        foundCustomTransformer = YES;
                    } else {
                        //try for hidden custom transformer
                        //嘗試隐藏轉換方法。
                        selectorName = [NSString stringWithFormat:@"__%@",selectorName];
                        selector = NSSelectorFromString(selectorName);
                        if ([valueTransformer respondsToSelector:selector]) {
                            foundCustomTransformer = YES;
                        }
                    }

                    //check if there's a transformer with that name
                    //檢查是否有一個具有該名稱的轉換方法
                    if (foundCustomTransformer) {

                        //it's OK, believe me...
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
                        //transform the value
                        //使用 JSONValueTransformer 進行類型轉換
                        jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];
#pragma clang diagnostic pop

                        [self setValue:jsonValue forKey: property.name];

                    } else {

                        // it's not a JSON data type, and there's no transformer for it
                        // if property type is not supported - that's a programmer mistaked -> exception
//                        @throw [NSException exceptionWithName:@"Type not allowed"
//                                                       reason:[NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name]
//                                                     userInfo:nil];
                        //解決api傳回的資料類型,與jsonmodel中聲明的資料類型不一緻時的crash
                        return NO;
                    }

                } else {
                    // 3.4) handle "all other" cases (if any)
                    //處理所有其他的case
                    [self setValue:jsonValue forKey: property.name];
                }
            }
        }
    }

    return YES;
}
           

三、JSONModel的源碼的特點

1、使用了runTime動态解析model資料類型

2、使用了AssociatedObject緩存

3、使用了NSScanner掃描字元串

4、使用了keyMapper映射

5、使用了KVC指派

6、使用了JSONValueTransformer類型轉換

四、JSONModel的特點

1、命名自動比對—-model的屬性名稱和伺服器傳回的一緻,比如關鍵字id我們可以使用keyMapper了來映射成其他的屬性名稱。

2、model中可以關聯其他的model,隻要指定對應的自身的類型

3、model中可以集合其他的model集合,這樣必須要實作@protocol協定

4、在一個Model中擷取伺服器傳回資料不同層級的資料

5、可以設定全局鍵映射

6、可以設定下劃線自動轉化為駝峰

7、可以設定可選屬性、忽略屬性

8、設定所有屬性為可選也表示可以所有可選屬性為nil

9、可以使用内置的HTTP連結

10、自定義資料處理,内嵌轉換比如類型之間的轉換

11、可以自定義處理特殊的屬性

12、可以自定義JSON的校驗

五、解析特點

我們隻解析比較重要的幾種特點

1、使用了runTime動态解析model資料類型

a、使用class_copyPropertyList方法去擷取所有model對象的屬性清單

b、使用property_getAttributes擷取目前屬性的property的encode string

c、使用property_getName去擷取屬性的name

2、使用了AssociatedObject緩存

AssociatedObject有三個相關的函數,我們的JSONModel主要使用的是objc_setAssociatedObject。

這個是objc_setAssociatedObject的調用的函數的過程

a、根據傳入的value來擷取new_value,判斷new_value是否存在。

b、根據我們對象的位址擷取對應的ObjectAssociationMap的位址,判斷該對象是否存在。就根據傳入的key擷取對象的關聯對象。

c、如果不存在,為該對象建立一個ObjectAssociationMap對象,存入新的關聯對象,設定好辨別位,再判斷就的關聯對象是否存在,如果存在釋放掉舊的關聯對象。

d、如果存在,持有舊的關聯對象,存入新的關聯對象。

e、再判斷判斷舊的關聯對象是否存在,如果存在就釋放掉舊的關聯對象。

以下是源碼

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        if (new_value) {
            // break any existing association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {
            // setting the association to nil breaks the association.
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
           

3、使用了NSScanner掃描字元串

NSScanner對象把NSString對象的的字元轉化成 number和string類型。在建立NSScanner對象的時候為它配置設定字元,當你從NSScanner對象擷取内容的時候,它會從頭到尾周遊字元串。也可以通過charactersToBeSkipped方法來忽略某些字元。也可以通過[scanner scanLocation]來設定設定掃描開始的地方。還可以通過caseSensitive方法來區分字元串中大小寫。

還可以通過一些方法來掃描一些特定類型的資料,比如:scanFloat—掃描單精度浮點型字元

scanHexDouble—掃描雙精度的十六進制類型

scanHexInt —- 掃描十六進制無符整型等等方法

還有就是一些掃描特定字元的方法

scanCharactersFromSet:intoString—- 掃描字元串中和NSCharacterSet字元集中比對的字元,是按字元單個比對的

scanUpToCharactersFromSet:intoString —- 掃描字元串,直到碰到NSCharacterSet字元串的時候就停下來等等,具體的一些方法可以去看 NSScanner官方文檔。

4、使用了JSONValueTransformer類型轉換

目前提供的方法有以下這些:

NSMutableString <-> NSString

NSMutableArray <-> NSArray

NS(Mutable)Array <- JSONModelArray

NSMutableDictionary <-> NSDictionary

NSSet <-> NSArray

BOOL <-> number/string

string <-> number

string <-> url

string <-> time zone

string <-> date

number <-> date

參考檔案:

https://blog.csdn.net/sodaslay/article/details/77870044

https://blog.csdn.net/u012496940/article/details/50032559

https://blog.csdn.net/game3108/article/details/52043661

http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/

https://www.jianshu.com/p/fbebd33d5b34

總結

如果有寫的不正确或者侵權的,希望大家給我提出來,我會及時修改。謝謝大家。

繼續閱讀