• JSONModel源码阅读笔记


    JSONModel是一个解析服务器返回的Json数据的库。

    http://blog.csdn.net/dyllove98/article/details/9050905

    通常服务器传回的json数据要通过写一个数据转换模块将NSDictionary转换为Model,将NSString数据转换为Model中property的数据类型。

    这样服务器如果要做修改,可能需要改两三个文件。

    JSONModel的出现就是为了将这种解析工作在设计层面完成。

    使用方法:参考连接

    对其源码的核心部分JSONModel.m做了源码阅读,笔记如下:

    -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
    函数中完成所有解析工作:如果有任何失误或者错误直接返回nil。

    -(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
    {
        //1、做有效性判断(dict是不是空啊,dict是不是真是一个NSDictionary)
        //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 errorInvalidData];
            return nil;
        }
    
        //create a class instance
        self = [super init];
        if (!self) {
            
            //super init didn't succeed
            if (err) *err = [JSONModelError errorModelIsInvalid];
            return nil;
        }
        
        //__setup__中通过调用__restrospectProperties建立类属性的映射表,并且存放在全局变量classProperties里面
        //->__restrospectProperties中利用runtime function搞出属性列表:
        //    ->获得属性列表class_copyPropertyList(得到objc_property_t数组)->对于每一个objc_property_t调用property_getName获得名称,property_getAttributes获得属性的描述(字符串)->通过解析字符串获得属性的类型、是否是Mutable、是否是基本的JSON类型等等
        //    ->调用[class superclass]获得父类继续获取列表
        //    ->列表保存在classProperties中备用
        //->调用+keyMapper获得key转换列表,生成JSONKeyMapper对象存入keyMapper。
        //do initial class setup, retrospec properties
        [self __setup__];
        
        //看看必传参数中是否在输入参数中都有。
        //check if all required properties are present
        NSArray* incomingKeysArray = [dict allKeys];
        NSMutableSet* requiredProperties = [self __requiredPropertyNames];
        NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
        
        //get the key mapper
        JSONKeyMapper* keyMapper = keyMappers[__className_];
        
        //transform the key names, if neccessary
        if (keyMapper) {
            //对比dict输入的keyName导入NSSet与keyMapper中JSONKeyMapper对象做keyName的转换。统一转换为对象的propertyname。
            NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
            NSString* transformedName = nil;
    
            //loop over the required properties list
            for (NSString* requiredPropertyName in requiredProperties) {
    
                //get the mapped key path
                transformedName = keyMapper.modelToJSONKeyBlock(requiredPropertyName);
                
                //chek if exists and if so, add to incoming keys
                if ([dict valueForKeyPath:transformedName]) {
                    [transformedIncomingKeys addObject: requiredPropertyName];
                }
            }
            
            //overwrite the raw incoming list with the mapped key names
            incomingKeys = transformedIncomingKeys;
        }
        
        //利用NSSet的isSubsetOfSet:将必传参数表与输入的keyName表对比。如果不是包含关系说明参数传的不够。
        //check for missing input keys
        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._className_, requiredProperties);
            
            if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];
            return nil;
        }
        
        //not needed anymore
        incomingKeys= nil;
        requiredProperties= nil;
        
        //从对象的classProperties列表中循环到dict中取值:(赋值使用KVO操作的setValue:forKey:来做的,这样会直接调用setter函数赋值)
        //loop over the incoming keys and set self's properties
        for (JSONModelClassProperty* property in [self __properties__]) {
            //对于每一个对象的property,通过keyMapper的转换找到对应dict property的dictKeyPath,找到值jsonValue。如果没有值,并且这个属性是Optional的就进行下一项property对比。
            //convert key name ot model keys, if a mapper is provided
            NSString* jsonKeyPath = property.name;
            
            if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name );
    
            //JMLog(@"keyPath: %@", jsonKeyPath);
            
            //general check for data type compliance
            id jsonValue = [dict valueForKeyPath: jsonKeyPath];
            
            //check for Optional properties
            if (jsonValue==nil && property.isOptional==YES) {
                //skip this property, continue with next property
                continue;
            }
            
            //对找到的值做类型判断,如果不是JSON应该返回的数据类型就报错。(注意:NSNull是可以作为参数回传的)
            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) *err = [JSONModelError errorInvalidData];
                return nil;
            }
                    
            //check if there's matching property in the model
            //JSONModelClassProperty* property = classProperties[self.className][key];
            
            //接着对property的属性与jsonValue进行类型匹配:
            if (property) {
                
                //如果是基本类型(int/float等)直接值拷贝;
                // 0) handle primitives
                if (property.type == nil && property.structName==nil) {
                    
                    //just copy the value
                    [self setValue:jsonValue forKey: property.name];
                    
                    //skip directly to the next key
                    continue;
                }
                
                //如果是NSNull直接赋空值;
                // 0.5) handle nils
                if (isNull(jsonValue)) {
                    [self setValue:nil forKey: property.name];
                    continue;
                }
    
                //如果是值也是一个JsonModel,递归搞JsonModel
                // 1) check if property is itself a JSONModel
                if ([[property.type class] isSubclassOfClass:[JSONModel class]]) {
                    
                    //initialize the property's model, store it
                    NSError* initError = nil;
                    id value = [[property.type alloc] initWithDictionary: jsonValue error:&initError];
    
                    if (!value) {
                        if (initError && err) *err = [JSONModelError errorInvalidData];
                        return nil;
                    }
                    [self setValue:value forKey: property.name];
                    
                    //for clarity, does the same without continue
                    continue;
                    
                } else {
                    
                    //如果property中有protocol解析将jsonValue按照protocol解析,如NSArray<JsonModelSubclass>,protocol就是JsonModelSubclass
                    // 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);
                        
                        //__transform:forProperty:函数功能:
                        //    ->先判断下protocolClass是否在运行环境中存在,如不存在并且property是NSArray类型,直接报错。否则,直接返回。
                        //    ->如果protocalClass是JsonModel的子类,
                        //    ->如果property.type是NSArray
                        //        ->判断一下是否是使用时转换
                        //        ->如果为使用时转换则输出一个JSONModelArray(NSArray)的子类
                        //        ->如果不是使用时转换则输出一个NSArray,其中的对象全部转换为protocalClass所对应对象
                        //    ->如果property.type是NSDictionary
                        //        ->将value转换为protocalClass所对应对象
                        //        ->根据key存储到一个NSDictionary中输出
                        jsonValue = [self __transform:jsonValue forProperty:property];
                        if (!jsonValue) {
                            if (err) *err = [JSONModelError errorInvalidData];
                            return nil;
                        }
                    }
                    
                    //如果是基本JSON类型(NSString/NSNumber)
                    // 3.1) handle matching standard JSON types
                    if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
                        
                        //如果是mutable的,做一份MutableCopy
                        //mutable properties
                        if (property.isMutable) {
                            jsonValue = [jsonValue mutableCopy];
                        }
                        
                        //set the property value
                        [self setValue:jsonValue forKey: property.name];
                        continue;
                    }
                    
                    //如果property.type是NSArray
                    // 3.3) handle values to transform
                    if (
                        //如果(类型没有匹配,并且jsonValue不为空)或者是Mutable的property(说明是特殊类型转换)
                        (![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))
                        ||
                        //the property is mutable
                        property.isMutable
                        ) {
                        
                        //利用JSONValueTransformer找到源类型
                        // 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);
                        
                        //用字符串拼出转换函数的名称字符串,到JSONValueTransformer中去搜索@SEL执行出正确类型
                        //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 if there's a transformer with that name
                        if ([valueTransformer respondsToSelector:selector]) {
                            
                            //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
                            
                            [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 nil;
                        }
                        
                    } else {
                        //哪儿都不是的直接存起来
                        // 3.4) handle "all other" cases (if any)
                        [self setValue:jsonValue forKey: property.name];
                    }
                }
            }
        }
        
        //最后调用validate:看看结果是不是有效,没问题就返回了。
        //run any custom model validation
        NSError* validationError = nil;
        BOOL doesModelDataValidate = [self validate:&validationError];
        
        if (doesModelDataValidate == NO) {
            if (err) *err = validationError;
            return nil;
        }
        
        //model is valid! yay!
        return self;
    }

    程序亮点:
    1、为了提高效率通过static的NSArray和NSDictionary进行解耦。
    2、JSONValueTransformer实现了一个可复用的类型转换模板。
    3、通过runtime function解析出property列表,通过property相关函数解析出名称,和Attributes的信息。
    4、NSScanner的使用
    5、NSSet的包含关系判断两个集合的交集
    6、利用[NSObject setValue:forKey:]的KVO操作赋值,可以直接调用setter函数,并且可以赋nil到property中
    7、适时给子类一个函数可以修改父类的一些行为

  • 相关阅读:
    linux启动流程
    树-二叉平衡树AVL
    算法导论第六章 堆排序
    算法导论基础(第一~五章)
    树-二叉查找树
    Java:基础
    【转】为什么C++编译器不能支持对模板的分离式编译
    压缩和解压缩命令
    Makefile编程
    1.什么是Mybatis?
  • 原文地址:https://www.cnblogs.com/geek6/p/4159789.html
Copyright © 2020-2023  润新知