[iOS开发]JSONModel源码学习

JSONModel对外给出了许多常见的初始化方法:

- (instancetype)initWithDictionary:(NSDictionary *)dict error:(NSError **)err;
- (instancetype)initWithData:(NSData *)data error:(NSError **)error;
// Create a new model instance and initialize it with the JSON from a text parameter. 
// The method assumes UTF8 encoded input text.
- (instancetype)initWithString:(NSString *)string error:(JSONModelError **)err;
// Create a new model instance and initialize it with the JSON from a text parameter using the given encoding.
- (instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError **)err;

接下来,我们看一看实现代码:

- (instancetype)initWithData:(NSData *)data error:(NSError *__autoreleasing *)err
{
    //check for nil input
    if (!data) {
        if (err) *err = [JSONModelError errorInputIsNil];
        return nil;
    }
    //read the json
    JSONModelError* initError = nil;
    id obj = [NSJSONSerialization JSONObjectWithData:data
                                             options:kNilOptions
                                               error:&initError];
    if (initError) {
        if (err) *err = [JSONModelError errorBadJSON];
        return nil;
    }
    //init with dictionary
    id objModel = [self initWithDictionary:obj error:&initError];
    if (initError && err) *err = initError;
    return objModel;
}
- (id)initWithString:(NSString*)string error:(JSONModelError**)err
{
    JSONModelError* initError = nil;
    id objModel = [self initWithString:string usingEncoding:NSUTF8StringEncoding error:&initError];
    if (initError && err) *err = initError;
    return objModel;
}

- (id)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err
{
    //check for nil input
    if (!string) {
        if (err) *err = [JSONModelError errorInputIsNil];
        return nil;
    }
    JSONModelError* initError = nil;
    id objModel = [self initWithData:[string dataUsingEncoding:encoding] error:&initError];
    if (initError && err) *err = initError;
    return objModel;
}

我们发现,这几个初始化方法最终实际上都还是调用了

- (id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err;

在看实现前,我们先看一看:

#pragma mark - associated objects names
static const char * kMapperObjectKey;
// 关联对象kMapperObjectKey
// 保存自定义的mapper
static const char * kClassPropertiesKey;
// 关联对象kClassPropertiesKey
// 用来保存所有属性信息的NSDictionary
static const char * kClassRequiredPropertyNamesKey;
// 关联对象kClassRequiredPropertyNamesKey
// 用来保存所有属性的名称NSSet
static const char * kIndexPropertyNameKey;

大致过程为:
首先,在这个模型类的对象被初始化的时候,遍历自身到所有的父类(直到JSONModel为止),获取所有的属性,并将其保存在一个字典里。获取传入字典的所有key,将这些key与保存的所有属性进行匹配。如果匹配成功,则进行kvc赋值。

此外,在load方法里,定义了它的支持:

+ (void)load {
    static dispatch_once_t once;
    dispatch_once(&once, ^{
        @autoreleasepool {
            //兼容的对象属性
            allowedJSONTypes = @[
                [NSString class], [NSNumber class], [NSDecimalNumber class], [NSArray class], [NSDictionary class], [NSNull class], //immutable JSON classes
                [NSMutableString class], [NSMutableArray class], [NSMutableDictionary class] //mutable JSON classes
            ];
            //兼容的基本类型属性
            allowedPrimitiveTypes = @[
                @"BOOL", @"float", @"int", @"long", @"double", @"short",
                @"unsigned int", @"usigned long", @"long long", @"unsigned long long", @"unsigned short", @"char", @"unsigned char",
                //and some famous aliases
                @"NSInteger", @"NSUInteger",
                @"Block"
            ];
            //转换器
            valueTransformer = [[JSONValueTransformer alloc] init];
            //自己的类型
            JSONModelClass = NSClassFromString(NSStringFromClass(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;
    }

    //check incoming data structure
    //第四步:检查映射结构是否能从我们传入的dict中找到对应的数据,如果不能找到,就返回nil,并且抛出错误
    if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
        return nil;
    }

    //import the data from a dictionary
    //第五步:根据传入的dict进行数据的赋值,如果赋值没有成功,就返回nil,并且抛出错误。
    if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
        return nil;
    }

    //run any custom model validation
    //第六步:根据本地的错误来判断是否有错误,如果有错误,就返回nil,并且抛出错误。
    if (![self validate:err]) {
        return nil;
    }

    //model is valid! yay!
    //第七步:返回self,model有效,耶嘿!
    return self;
}

我们看一下其中的第三步:

//第三步:核心的代码,通过init方法初始化映射property
self = [self init];

看一下它的实现:

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

调用了__setup__方法。我们来看它的实现:

- (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是否存在和是否进行映射过,如果没有进行映射就使用AssociateObject方法进行映射
    id mapper = [[self class] keyMapper];
    if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
        //第三步:进行AssociateObject映射
        objc_setAssociatedObject(
                                 self.class,
                                 &kMapperObjectKey,
                                 mapper,
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }
}

key mapper主要是用来针对某些json字段名和model数据名不一致的情况。
比如"com.app.test.name":"xxx""test_name":"xxx"这样的情况,可能对应的model数据字段名为name,那如何讲着两个值进行映射,就通过key mapper来完成。
接下来解析一下__inspectProperties方法,介绍怎么进行映射property的缓存。先看实现:

//inspects the class, get's a list of the class properties
//检查类,获取类属性的列表
//它的任务是保存所有需要赋值的属性。
- (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);

        //loop over the class properties
        // 遍历property
        for (unsigned int i = 0; 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的属性
            // 通过property_getAttributes获取property的encode string,解析encode string可以解析出具体property的类型
            const char *attrs = property_getAttributes(property);
            NSString* propertyAttributes = @(attrs);
            NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];

            //ignore read-only properties
            //忽略只读属性
            if ([attributeItems containsObject:@"R"]) {
                continue; //to next property
            }
            
            // 扫描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"]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
                        p.isIndex = YES;
#pragma GCC diagnostic pop

                        objc_setAssociatedObject(
                                                 self.class,
                                                 &kIndexPropertyNameKey,
                                                 p.name,
                                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                                 );
                    } else if([protocolName isEqualToString:@"Ignore"]) {
                        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
            // 属性必须是基本类型,比如int float等
            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 mistaken -> 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;
            }
            
            Class customClass = [[self class] classForCollectionProperty:nsPropertyName];
            if (customClass) {
                p.protocol = NSStringFromClass(customClass);
            }

            //few cases where JSONModel will ignore properties automatically
            //在少数情况下,JSONModel 会自动忽略属性
            if ([propertyType isEqualToString:@"Block"]) {
                p = nil;
            }

            //add the property object to the temp index
            //将属性对象添加到临时索引
            // 通过kvc去设置相应的值
            if (p && ![propertyIndex objectForKey:p.name]) {
                [propertyIndex setValue:p forKey:p.name];
            }

            // generate custom setters and getter
            //生成自定义设置器和获取器
            if (p)
            {
                NSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];

                // getter
                SEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);

                if ([self respondsToSelector:getter])
                    p.customGetter = getter;

                // setters
                p.customSetters = [NSMutableDictionary new];

                SEL genericSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@WithJSONObject:", name]);

                if ([self respondsToSelector:genericSetter])
                    p.customSetters[@"generic"] = [NSValue valueWithBytes:&genericSetter objCType:@encode(SEL)];

                for (Class type in allowedJSONTypes)
                {
                    NSString *class = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:type]);

                    if (p.customSetters[class])
                        continue;

                    SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@With%@:", name, class]);

                    if ([self respondsToSelector:setter])
                        p.customSetters[class] = [NSValue valueWithBytes:&setter objCType:@encode(SEL)];
                }
            }
        }

        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
                             );
}

通过注释,我们可以大致了解整个过程了。大概提取为:

  1. 先是获取当前class的property列表和个数
  2. 然后再遍历这些property
  3. 把我们的property通过一个局部变量进行赋值–JSONModelClassProperty,这个是JSONModel提供的类,来解析每个property。
  4. 获取property的名称给当前这个局部变量
  5. 获取这个property的属性
  6. 扫描property属性
  7. 设置property的类型
  8. 判断并设置property的是否是可变的
  9. 判断property的是否我们允许的json类型
  10. 解析protocol的string
  11. 检查property是否为structure
  12. 判断property是不是Optional
  13. 判断property是不是Ignored
  14. 判断property是不是只读属性
  15. 通过kvc去设置相应的值
  16. 使用AssociateObject进行缓存

需要注意几点:
作者利用一个while函数,获取当前类和当前类的除JSONModel的所有父类的属性保存在一个字典中。在将来用于和传入的字典进行映射。
作者用JSONModelClassProperty类封装了JSONModel的每一个属性。这个类有两个重要的属性:一个是name,它是属性的名称。另一个是type,它是属性的类型(例如NSString)。
作者将属性分为了如下几个类型:

  • 对象(不含有协议)。
  • 对象(含有协议,属于模型嵌套)。
  • 基本数据类型。
  • 结构体。

这里面用到了大量的runtime运行时方法。

objc_property_t *properties = class_copyPropertyList(class, &propertyCount);

class_copyPropertyList()就是将一个类的属性copy出来。可以使用函数class_copyPropertyList()protocol_copyPropertyList()分别检索与类(包括加载的类别)和协议关联的属性数组。简单来说就是:
使用runtimeclass_copyPropertyList方法去获得所有model对象的property列表,再使用
property_getAttributes获得propertyencode string,通过解析encode string去获得property对象的正确含义。
在解析的过程中,使用NSScanner去扫描encode string,并使用JSONModelClassProperty的结构体去保存相关信息。
其中对于protocol的使用较为特殊,在这里的protocol并非我们平常当作接口抽象的作用,而是单纯的为了让encode string中增加相应的字段,可以在解析与赋值的时候给予特定的含义。

接下来研究一下- (BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err;方法。看一下实现:

- (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].mutableCopy;
    NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];

    //transform the key names, if necessary
    //第二步:判断是否存在keyMapper映射,如果存在,在对应的set中找到对应的key进行替换
    //即如果用户自定义了mapper,则进行转换
    if (keyMapper || globalKeyMapper) {

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

        //loop over the required properties list
        //这个是遍历所需要的属性列表
        for (JSONModelClassProperty* property in [self __properties__]) {
            
            //获取mapped的关键值transformedName
            transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;

            //check 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;
}

查值的作用主要就是为了能够检查是否model的所有property是否都能够被赋值,如果不能则说明缺少值则抛出错误。这边主要就是使用了NSSet,将dictionary的所有key存入一个setincomingKeys,并且将key mapper映射名进行替换。将刚解析出来的model所有propertyname也存入一个setrequiredProperties,判断两者是不是包含关系。此外,如果存在了用户自定义的mapper,则需要按照用户的定义来进行转换。

//从字典里获取值并赋给当前模型对象
- (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 to model keys, if a mapper is provided
        //第二步:获取keyMapper的映射,获取真正的值
        NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : 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
                if (jsonValue != [self valueForKey:property.name]) {
                    [self setValue:jsonValue forKey: property.name];
                }

                //skip directly to the next key
                continue;
            }

            // 0.5) handle nils
            if (isNull(jsonValue)) {
                if ([self valueForKey:property.name] != nil) {
                    [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;
                }
                if (![value isEqual:[self valueForKey:property.name]]) {
                    [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 transform 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赋值
                    if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                        [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) {
                        IMP imp = [valueTransformer methodForSelector:selector];
                        id (*func)(id, SEL, id) = (void *)imp;
                        //使用 JSONValueTransformer 进行类型转换
                        jsonValue = func(valueTransformer, selector, jsonValue);

                        if (![jsonValue isEqual:[self valueForKey:property.name]])
                            [self setValue:jsonValue forKey:property.name];
                    } else {
                        if (err) {
                            NSString* msg = [NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name];
                            JSONModelError* dataErr = [JSONModelError errorInvalidDataWithTypeMismatch:msg];
                            *err = [dataErr errorByPrependingKeyPathComponent:property.name];
                        }
                        //解决api返回的数据类型,与jsonmodel中声明的数据类型不一致时的crash
                        return NO;
                    }
                } else {
                    // 3.4) handle "all other" cases (if any)
                    //处理所有其他的case
                    if (![jsonValue isEqual:[self valueForKey:property.name]])
                        [self setValue:jsonValue forKey:property.name];
                }
            }
        }
    }
    return YES;
}

循环遍历model的每一个解析出来的property结构,首先从dictioanry拿出真正对应propertyvalue,进行value一系列的值判断。value可用的情况下,就开始进行赋值,有setter方法的通过setter方法赋值,基础类型intfloat等直接赋值,如果property又是一个JSONModel,就递归先将子Model进行整体解析。如果包含protocol字段,则表明内部是一个array或者dictionary,并包含这个protocol字段的对象解析。对于其他情况,应该是一种类型的转换,通过获取值类型和property类型,调用相应的转换方法进行赋值。
其中值得一提的就是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

总的来说JSONModel的源代码有以下特点:

  • Runtime动态解析model数据类型
  • AssociatedObject缓存
  • keyMapper映射
  • NSScanner扫描String
  • JSONValueTransformer类型转换
  • KVC附值

JSONMOdel有以下优点:

  • 命名自动匹配—-model的属性名称和服务器返回的一致,比如关键字id我们可以使用keyMapper了来映射成其他的属性名称。
  • model中可以关联其他的model,只要指定对应的自身的类型
  • model中可以集合其他的model集合,这样必须要实现@protocol协议
  • 在一个Model中获取服务器返回数据不同层级的数据
  • 可以设置全局键映射
  • 可以设置下划线自动转化为驼峰
  • 可以设置可选属性、忽略属性
  • 设置所有属性为可选也表示可以所有可选属性为nil
  • 可以使用内置的HTTP链接
  • 自定义数据处理,内嵌转换比如类型之间的转换
  • 可以自定义处理特殊的属性
  • 可以自定义JSON的校验

看到人家一个图总结的挺好的,自己画一下看看:
[iOS开发]JSONModel源码学习_第1张图片

你可能感兴趣的:(OC,基础知识,objective-c,开发语言,ios)