******************第一段*********************
-(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;
}
JSONModel源码学习<一>
最新推荐文章于 2022-04-24 12:42:53 发布