JSONModel源码学习<一>

来源:互联网 发布:世界大战红包软件 编辑:程序博客网 时间:2024/04/27 19:15
******************第一段*********************-(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;    }//前面两个判断传入的dict是否为空或者是否是 [NSDictionary class]    //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;}//init初始化之后的 setup-(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                                 );//绑定到类上    }}-(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* 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            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                             );}//这里还没有处理如果成员变量是jsonmodel类型,即模型嵌套的处理//**************华丽的分割线,我胡汉三又回来了,哈哈哈**************//回到__setup__,回到init,回到initWithDictionary: error:-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err{。。。//接着上面的initWithDictionary: error:{    self = [self init];    if (!self) {//是否初始化成功        //super init didn't succeed        if (err) *err = [JSONModelError errorModelIsInvalid];        return nil;    }    //check incoming data structure    //处理需要转换名字的成员变量,并判断传入的字典里有没有model里所有的requiredProperty,如果不全部包括,返回NO    if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {        return nil;    }    ...}-(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;}//***********恩,又分割了,part3**********-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err{//接上面处理好需要转换名字的成员变量名字和。。。    //import the data from a dictionary    //赋值    if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {        return nil;    }    //run any custom model validation    //[self validate:err]应该是预留的接口,现在永远返回YES,翻译:validate-验证,确认    if (![self validate:err]) {        return nil;    }    //model is valid! yay!    return self;}//赋值-(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;}
0 0