美文网首页
jsonModel源码分析

jsonModel源码分析

作者: IOShzz | 来源:发表于2016-11-15 16:08 被阅读0次

    JSONModel是我们要讲解的重点,我们首先从它的初始化方法谈起。

    182380-4c4c9ae0a9d4a9d7.jpg
    -(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
    -(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
    -(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
    -(instancetype)initWithData:(NSData *)data error:(NSError **)error;
    

    designated initializer

    粗略一看,四个初始化方法,太可怕了。但是我们知道在iOS的设计理念里,有一种designated initializer的说法


    6941baebjw1eozmnjcsvaj21060ueqbx.jpg

    在自己的开发过程中,合理地遵守和运用designated initializer会减少许多重复代码。
    并且理解了这一个概念,对整个Cocoa框架的理解也有帮助。 例如UIViewController的Designated initializer是

    - (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle

    但是可能有人会发现,如果你直接使用 [[viewController alloc]init]来生产Controller,且你是使用XIB来组织界面的,那么最后你得到的ViewController的View还是来自XIB的。这背后的原因就是Designated initializer帮你完成了这个工作。
    因此,我们挑一个 initWithDictionary看起。
    废话不多说直接看源码

    -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
    {
    
        //检查是否为空
        if (!dict) {
            if (err) *err = [JSONModelError errorInputIsNil];
            return nil;
        }
    
           //类型判断
        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
        self = [self init];//转入重写的init方法
        。。。。。。。后面的在init之后,先看init方法
    }
    
    **************************************************************************************************************
    //重写的init方法
    -(id)init
    {
        self = [super init];
        if (self) {
            //do initial class setup
            [self __setup__];
        }
        return self;
    }
    
    
    **************************************************************************************************************
    -(void)__setup__
    {
        //if first instance of this model, generate the property list
        if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {//是否第一次初始化这个模型类,标准是是否绑定了kClassPropertiesKey,kClassPropertiesKey取出来是数组(存放类的成员变量)
            [self __inspectProperties];//去遍历成员变量列表,并把遍历好的放到数组中,以kClassPropertiesKey为键绑定:objc_setAssociatedObject,
        }
        //if there's a custom key mapper, store it in the associated object
        id mapper = [[self class] keyMapper];//取出需要进行转换的成员变量名,比如id-->ID,是重写keyMapper返回的JSONKeyMapper类型
        if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {//如果有,并且是第一次初始化之前没有进行过绑定
            objc_setAssociatedObject(
                                     self.class,
                                     &kMapperObjectKey,
                                     mapper,
                                     OBJC_ASSOCIATION_RETAIN // This is atomic
                                     );//绑定到类上
        }
    }
    
    

    这里需要注意的是 objc_setAssociatedObject这个方法 之前可能很少用到。简单来说类目是给对象增加方法 objc_setAssociatedObject是为类或者对象增加属性
    创建关联要使用到Objective-C的运行时函数:objc_setAssociatedObject来把一个对象与另外一个对象进行关联。该函数需要四个参数:源对象,关键字,关联的对象和一个关联策略。当然,此处的关键字和关联策略是需要进一步讨论的。
    ■ 关键字是一个void类型的指针。每一个关联的关键字必须是唯一的。通常都是会采用静态变量来作为关键字。
    ■ 关联策略表明了相关的对象是通过赋值,保留引用还是复制的方式进行关联的;还有这种关联是原子的还是非原子的。这里的关联策略和声明属性时的很类似。这种关联策略是通过使用预先定义好的常量来表示的。 下面是实例代码

    //
    //  main.m
    //  objc_getAssociatedObject
    //
    //  Created by 何壮壮 on 16/11/4.
    //  Copyright © 2016年 何壮壮. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    #import "AppDelegate.h"
    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    #import "person.h"
    // 表示关联关系的key,主要目的是用来索引
    const NSString *associatedKey = @"associate_nsarray_with_nsstring_key";
    
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            
            
            
            
            NSArray *array = [[NSArray alloc]initWithObjects:@"1", @"2", @"3", nil];
           
            NSString * overview = @"Hello world";
     // 从array中获取被关联的对象string
    
     // 注意,这里就没有string这个对象任何事了
    
     // string其实已经变成了array的一个属性值
            objc_setAssociatedObject(array, &associatedKey, overview, OBJC_ASSOCIATION_RETAIN);
            
            NSString *associatedObject = (NSString *)objc_getAssociatedObject(array, &associatedKey);
            
            NSLog(@"associatedObject:%@",associatedObject);
            
            return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
        }
    }
    
    

    这段代码使用AssociateObject的缓存判断kClassPropertiesKey就知道该model对象是否有进行过解析property,没有的话进行解析,同时取出model的key mapper,也同样进行缓存。
    key mapper主要是用来针对某些json字段名和model数据名不一致的情况。
    比如"com.app.test.name":"xxx","test_name":"xxx"这样的情况,可能对应的model数据字段名为name,那如何讲着两个值进行映射,就通过key mapper来完成。
    主体的解析代码如下:

    遍历成员变量列表并设置关联

    -(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));
    
            unsigned int propertyCount;
            objc_property_t *properties = class_copyPropertyList(class, &propertyCount);//取出成员变量列表
    
            //loop over the class properties
            for (unsigned int i = 0; i < propertyCount; i++) {
           //JSONModelClassProperty包涵解析与赋值时候的所有判断
                JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
    
                //get property name
                objc_property_t property = properties[i];
                const char *propertyName = property_getName(property);
                p.name = @(propertyName);//成员变量名
    
                //JMLog(@"property: %@", p.name);
    
                //get property attributes
                //核心,通过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 包含 R 的为只读的,不解析
                if ([attributeItems containsObject:@"R"]) {
                    continue; //to next property
                }
    
                //check for 64b BOOLs 检查是否是c中字符char ,char 的头是Tc
                if ([propertyAttributes hasPrefix:@"Tc,"]) {
                    //mask BOOLs as structs so they can have custom convertors
                    p.structName = @"BOOL";
                }
    
                scanner = [NSScanner scannerWithString: propertyAttributes];
    
                //JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
                //把scanner的scanLocation(我理解为光标)移动到T之后
                [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]) {
                //继承自NSObject的类会有 @/"  前缀,例如  "T@\"NSString<PropertyProtocol><PropertyProtocol2>\",C,N,V_type",,移动到@/"之后
    
                    [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
                                            intoString:&propertyType];//扫描到"<(有遵循的协议)或者"(没有遵循的协议)之后,这里的协议只指声明成员变量的时候声明遵循的协议,得到的就是class类,<>里面是遵循的协议,见上面例子
    
                    //JMLog(@"type: %@", propertyClassName);
                    p.type = NSClassFromString(propertyType);
                    p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);//是否是可变的
                    p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];//是否是允许的类allowedJSONTypes = @[NSString class], [NSNumber class], [NSDecimalNumber class], [NSArray class], [NSDictionary class], [NSNull class],[NSMutableString class], [NSMutableArray class], [NSMutableDictionary class]];
    
                    //read through the property protocols
                    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 {
                            p.protocol = protocolName;
                        }
    
                        [scanner scanString:@">" intoString:NULL];
                    }
    
                }
                //check if the property is a structure
                else if ([scanner scanString:@"{" intoString: &propertyType]) {//或者是结构体,CGPoint,CGRect之类的,具体成员变量里为什么会出现这个现在还没有遇到过,官方文档说包含{的是:@property struct YorkshireTeaStruct structDefault;T{YorkshireTeaStruct="pot"i"lady"c},VstructDefault/@property YorkshireTeaStructType typedefDefault;T{YorkshireTeaStruct="pot"i"lady"c},VtypedefDefault/@property union MoneyUnion unionDefault;T(MoneyUnion="alone"f"down"d),VunionDefault
                    [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
                                        intoString:&propertyType];
    
                    p.isStandardJSONType = NO;
                    p.structName = propertyType;
    
                }
                //the property must be a primitive
                else {//原始 数据类型,允许的原始数据类型allowedPrimitiveTypes = @[@"BOOL", @"float", @"int", @"long", @"double", @"short",@"NSInteger", @"NSUInteger", @"Block"];
                    //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];//取出基本数据类型的全称@{@"f":@"float", @"i":@"int", @"d":@"double", @"l":@"long", @"c":@"BOOL", @"s":@"short", @"q":@"long",@"I":@"NSInteger", @"Q":@"NSUInteger", @"B":@"BOOL",@"@?":@"Block"};
                    //判断是否是允许的基本数据类型
                    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);
    
                //基本数据类型定义的时候不能遵循协议,通过类方法判断成员变量遵循的协议
                if([[self class] propertyIsOptional:nsPropertyName]){
                    p.isOptional = YES;
                }
    
                if([[self class] propertyIsIgnored:nsPropertyName]){
                    p = nil;
                }
    
                NSString* customProtocol = [[self class] protocolForArrayProperty:nsPropertyName];
                if (customProtocol) {
                    p.protocol = customProtocol;
                }
    
                //few cases where JSONModel will ignore properties automatically 忽略block的成员变量
                if ([propertyType isEqualToString:@"Block"]) {
                    p = nil;
                }
    
                //add the property object to the temp index
                if (p && ![propertyIndex objectForKey:p.name]) {
                    [propertyIndex setValue:p forKey:p.name];
                }
            }
    
            free(properties);//注意:这里释放properties,否则会造成内存泄露
            //ascend to the super of the class
            //(will do that until it reaches the root class - JSONModel)
            //继续遍历直到基类JSONMedol
            class = [class superclass];
        }
    
        //finally store the property index in the static property index
        //将遍历的结果绑定到类上,这样保证只遍历一次
        objc_setAssociatedObject(
                                 self.class,
                                 &kClassPropertiesKey,
                                 [propertyIndex copy],
                                 OBJC_ASSOCIATION_RETAIN // This is atomic
                                 );
    }
    

    这么多代码,其实总结起来就几个步骤:
    获取当前类的的property list,通过class_copyPropertyList runtime的方法
    遍历每一个propery,解析他们的属性,这里的属性包括是否只读、类型、是否是weak类型,是否是原子性的等等,如果不了解,可以看如下的表格:
    | Code | Meaning |
    | :————-: |:————-
    | R | The property is read-only (readonly).
    | C | The property is a copy of the value last assigned (copy).
    | & | The property is a reference to the value last assigned (retain).
    | N | The property is non-atomic (nonatomic).
    | G| The property defines a custom getter selector name. The name follows the G (for example, GcustomGetter,).
    | S| The property defines a custom setter selector name. The name follows the S (for example, ScustomSetter:,).
    | D | The property is dynamic (@dynamic).
    | W | The property is a weak reference (__weak).
    | P | The property is eligible for garbage collection.
    | t| Specifies the type using old-style encoding.

    根据解析结果,检测是不是合法,如果合法创建对应的JSONModelClassProperty并赋值相应的属性值。

    然后重复执行,查看完当前的类就去查询其父类,直到没有为止。

    最后将解析出来的property list通过Associate Object给赋值,这和我们刚刚在 setup中看到的相呼应。
    简单来说通过**class_copyPropertyList
    获取属性列表,得到objc_property_t
    **数组。

    You can use the functions class_copyPropertyList and protocol_copyPropertyList to retrieve an array of the properties associated with a class (including loaded categories) and a protocol respectively

    通过遍历**objc_property_t
    调用property_getName
    获得名称,property_getAttributes
    **获得属性的描述(字符串)。

    You can use the property_getAttributes function to discover the name and the @encode type string of a property.

    存储信息。
    最后
    class = [class superclass]
    获取父类,如果父类是JSONModel的子类,继续进行上述三步。
    最后的最后
    objc_setAssociatedObject

    字典MatchingModel确保是KeyMapper的子集

    
    -(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
    {
        //check if all required properties are present
        NSArray* incomingKeysArray = [dict allKeys];
        NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;//[self __requiredPropertyNames]会对__properties__遍历取出isOptional=NO的,并且通过objc_setAssociatedObject,这样可以只在第一次遍历,后面直接取
        NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
    
        //transform the key names, if neccessary
        //处理有需要转换名字的成员属性
        if (keyMapper || globalKeyMapper) {
    
            NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
            NSString* transformedName = nil;
    
            //loop over the required properties list
            for (JSONModelClassProperty* property in [self __properties__]) {
    
                transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
    
                //chek if exists and if so, add to incoming keys
                id value;
                @try {//这里没有懂为什么用valueForKeyPath:,没有用valueForKey:
                    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
        //利用NSSet的子集 看看要求必有的是否是传入字典的子集
        if (![requiredProperties isSubsetOfSet:incomingKeys]) {
    
            //get a list of the missing properties
            //取出没有的成员列表变量,如果有error,赋给error,并返回NO
            [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;
    }
    
    

    如果得到的objc_property_t
    数组是dict
    的超集,说明我们的Model中有属性是赋不到值的,往往程序就会crash在这里。解决方法,属性后加入<Optional>
    表示可以为空。或者
    设置所有属性为可选
    **。

    @implementation ProductModel
    +(BOOL)propertyIsOptional:(NSString*)propertyName
    { 
      return YES;
    }
    @end
    

    在json数据中例如有字段名为id或者description的语言关键字,语法中式不能这么写的

    @property (assign, nonatomic) int id;
    
    

    只能写这种

    @property (assign, nonatomic) int xxxID;
    

    而这么写了在此func种就会由于**objc_property_t
    中不包含xxxID
    **crash,于是就有了keyMapper可以避免这个问题。

    //赋值
    -(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
    {
        //loop over the incoming keys and set self's properties
        for (JSONModelClassProperty* property in [self __properties__]) {
    
            //convert key name ot model keys, if a mapper is provided
            NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper importing:YES] : property.name;
            //JMLog(@"keyPath: %@", jsonKeyPath);
    
            //general check for data type compliance
            id jsonValue;//从jsonDict中以property取值
            @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;
            //判断返回的数据类型是否是允许的数据类型
            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
            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
                //检查成员变量的set方法,property.setterType默认为kNotInspected(未检查过的),还有kNo(没有set方法),kCustom(正常的set方法,即:setPropertnameWith:),如果有赋值,并返回yes
                if ([self __customSetValue:jsonValue forProperty:property]) {
                    //skip to next JSON key
                    //有正常的set赋值方法,赋值,continue
                    continue;
                };
    
                // 0) handle primitives
                //赋值基本数据类型:不是对象也不是结构体,int、float之类的
                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
                //判断是否为nil,或者NSNull类型
                if (isNull(jsonValue)) {
                //字典值为空,self之前的值非空,赋空
                    if ([self valueForKey:property.name] != nil) {
                        [self setValue:nil forKey: property.name];
                    }
                    continue;
                }
    
                // 1) check if property is itself a JSONMode
                if ([self __isJSONModelSubClass:property.type]) {
                    //处理jsonModel嵌套
                    //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 transofrm for it
                    if (property.protocol) {
    
                        //JMLog(@"proto: %@", p.protocol);
                        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
                    if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
    
                        //mutable properties
                        if (property.isMutable) {
                            jsonValue = [jsonValue mutableCopy];
                        }
    
                        //set the property value
                        if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                            [self setValue:jsonValue forKey: property.name];
                        }
                        continue;
                    }
    
                    // 3.3) handle values to transform
                    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)
                        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
                        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
                            jsonValue = [valueTransformer performSelector:selector withObject:jsonValue];
    #pragma clang diagnostic pop
    
                            if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                                [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];
                            return NO;
                        }
    
                    } else {
                        // 3.4) handle "all other" cases (if any)
                        if (![jsonValue isEqual:[self valueForKey:property.name]]) {
                            [self setValue:jsonValue forKey: property.name];
                        }
                    }
                }
            }
        }
    
        return YES;
    }
    

    整个func主要就做赋值工作
    从对象的 **properties
    ** 数组中,循环到 **dict
    ** 中取值,通过 **KVC
    ** 操作。循环中,使用 **keyMapper
    ** 的映射找对对应的字典属性的 **keyPath
    ** ,获取 **jsonValue
    **

    ps:因为是通过KVO赋值,根本是使用keyPath,所以keyMapper可以解决当model中属性名和json串中属性名不同的问题

    赋值时分以下几种情况:

    1. 判断是不是空值,如果是空值并且属性非optional,error。
    2. 判断是不是合法的json类型,如果不是,error。
    3. 如果是属性:
      3.1 如果有自定义setter调用自定义setter,然后continue。
      3.2 如果没有自定setter时,属性是基本数据类型,int,double等,直接用KVC赋值。
      3.3 如果没有自定setter时,属性是一个JSONModel,就解析这个JSONModel(递归),最终还是使用KVC赋值。
      3.4 如果没有自定setter时,属性带有protocol字段,说明是个字典或者数组,将他遍历后解析。
      3.5 如果没有自定setter时,属性和json串中字段均为标准json类型,直接用KVC赋值。
      3.6 如果没有自定setter时,属性和json串中字段不同,使用JSONValueTransformer进行转换。
      3.7 处理其他case,使用KVC直接赋值。
    首先在OC中拥有很多簇类。
    当我们debug的时候有时会发现一个NSString在底层不是NSString,有时是NSPlaceholderString,有时又是别的。
    因为NSString在设计上得时候采用了抽象工厂的设计模式,内部是一个簇类(class cluster)。
    这也是NSString,NSArray,NSDictionary什么的官方不建议去继承的原因。
    使用JSONValueTransformer,就是由于这些簇类。需要使用JSONValueTransformer得到真正的类型。
    

    关于类簇以及抽象工厂模式可以看这篇文章

    http://www.jianshu.com/p/0dfd1b66233a

    相关文章

      网友评论

          本文标题:jsonModel源码分析

          本文链接:https://www.haomeiwen.com/subject/hdakpttx.html