JSONModel source analysis

The source of this series of analytical sharing is now fifth. These five explanations are some of the popular iOS framework view open source framework. From the beginning of this article, I intend to gradually deepen the difficulty to explain some of the model layer and network layer related to open source framework.

After much deliberation, or from the JSONModel to begin.

First of all, because the framework is more popular, there are nearly 6000 stars on the GitHub, and I am familiar with the use of this framework is also more familiar with. Another point is that the framework of the use of run-time knowledge, you want to know the running time of the child’s shoes is very helpful.

The framework of the core code is not a lot, mainly the wrong type of judgment and fault tolerance accounted for a lot of content. After I read it again, feel the author thinking preciseness is very worthy of our study, the author established a special display error (NSError) class, which encapsulates many types of errors, and generate the framework also allows users according to their needs from the definition of error types and prevent the final model, in the post this paper will explain in detail.

The source explained before, it is first necessary to not use JSONModel students through practical examples to introduce the use of it (and later part of source code analysis is a combination of these examples are given, for example to help speed up the understanding):

Usage method

1 the most basic use

The first is simply passing in a dictionary and converting it into a model:
first we need to define our own model class:

@interface Person: JSONModel @property (nonatomic, copy) NSString *name; @property (nonatomic, copy) NSString *sex; @property (nonatomic, assign) NSInteger age; @end

Then use the dictionary to convert to the model:

NSDictionary *dict = "name" @{@ @ @: "Jack", "age", "gender": @ @23, @ male, NSError}; *error; Person *person = [[Person alloc] initWithDictionary:dict error:& error];

Output:

< Person> [name]: Jack [sex]: male 23: < /Person> [gender];

It can be seen that the use of the framework is very convenient, a line of code will be converted to the model.
but the framework is much more than that:

2 conversion property name

Sometimes, change into the dictionary (for example, key interface reconstruction, but the model we attribute the reason) front side have written may not be easy to be modified (because of what the business logic is very complex, so this time), it is best to have a transfer function.

Here is an example: the original dictionary of the gender key into sex, which requires us to define a conversion of mapper (JSONKeyMapper):

@implementation + Person (JSONKeyMapper *) keyMapper return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{{@ @ "gender": "sex",}]};

As a result, JSONKeyMapper will automatically help us do the conversion.
in order to verify the effect, we modify the incoming dictionary in the gender field for sex:

NSDictionary *dict = "name" @{@ @ @: "Jack", "age", "sex": @ @23, @ male, NSError}; *error; Person *person = [[Person alloc] initWithDictionary:dict error:& error];

Look at the output again:

< Person> [name]: Jack [age]: 23 [gender]: male < /Person>

Not affected by changes in the key value in the incoming dictionary, right?

3 custom error

In addition to some of the errors in the framework of their own processing (such as the introduction of the object is not a dictionary, etc.), the author of the framework also allows us to define our own mistakes.

For example, when age corresponds to a value of less than 25, print out the Too young and block the conversion of the model:

First, we add in the model implementation file:

- (BOOL) validate: (NSError) {if ([super validate:error] error! Return NO if (self.age); < 25) {*error = [NSError errorWithDomain:@ "Too young!" code:10 userInfo:nil]; NSError *errorLog = *error; NSLog (@ "% @", errorLog.domain); return NO;} return YES;}

Look at the results:

2017-02-20 16:14:54.217 Too young! 2017-02-20 jsonmodel_demo[32942:1967433] jsonmodel_demo[32942:1967433] (null) (16:14:54.217)

The error was printed and the model was not converted.

4 model nesting

Sometimes, we need to add an array to the model, and the element in the array is another object.

For example, we let the above Person object contains an array of Friends, which is the element of the object Friend, that is, friend information. To implement the nested model, we only need to add a protocol to the original model class Friend:

#import "JSONModel.h" @protocol Friend; @interface Friend: JSONModel @property (nonatomic, copy) NSString *name @property (nonatomic, assign); NSInteger age; @end @interface Person JSONModel @property (nonatomic, copy) NSString *name @property (nonatomic, assign); NSInteger age; @property (nonatomic copy, NSString *gender @property (nonatomic); strong, NSArray<); Friend> *friends; / / array, nested @end model

But also in the Person file to add this section of the code:

@implementation Friend @end

Be careful´╝ü If you do not add, it will cause the program to crash.

Finally, in the use of, we only need to hold an array of dictionaries can be introduced:

NSArray *array = "name" @[@{@ @ @: "Peter", "age": @35, NSDictionary]},; *dict = @{@ @ "name": "Jack", "age": @ @23, @ sex @ @: "male", "friends": array. / / friends list (nested)}; NSError * error; Person *person = [[Person alloc] initWithDictionary:dict error:& error]; NSLog (@ "% @", person);

Output results:

< Person> [age]; 23 [gender]: male [friends]: ("< Friend> /n [name]; Peter/n [age]; 35/n< /Friend>"): [name]: Jack < /Person>

We can see that the person object contains an array, the array has only one element, corresponding to the information in the dictionary array.

OK, in this way, we have been able to master the main use of the framework, and now began to explain the code in detail:

Source analysis

The source code analysis is mainly around the initWithDictionary:error: to expand, in this method the author has done all the fault tolerance and model transformation.

According to the old rules, the first flow chart:

JSONModel source analysis
dictionary -> model

The corresponding implementation of the flowchart is:

- (ID) initWithDictionary: (NSDictionary*) Dict error: (NSError** ERR) {/ / methods 1 Parameters for nil (if! Dict) {if (ERR) *err = [JSONModelError errorInputIsNil]; return nil;} / / 2 parameter is not nil, but not if ([dict isKindOfClass:[NSDictionary class]] dictionary!) {if (ERR) *err [JSONModelError errorInvalidDataWithMessage:@ = "Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an'NSDictionary'."]; return nil;} / / self = [self init] 3 initialization method; if (self!) {/ / if failed to initialize (ERR) *err = [JSONModelError errorModelIsInvalid]; return nil;} / / method 4 check user defined In the set of attribute model is greater than the incoming dictionary set (if more than key, returns NO (if) [self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper! Error:err]) {return nil;} / / 5 core methods: if property mapping and model dictionary of the key ([self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES! Error:err]) {return nil;} 6 / / method can override the [self method of the validate:err] and return to NO, allowing users to custom error and stop the return of model if (! [self validate:err]) {return nil;} / / 7 finally passed! Successfully returned model return self;}

Which,

  • Method 1-4: is the discovery and treatment of errors.
  • Method 5: is the real mapping.
  • Method 6: is the author of the user to define their own error method, if the user’s own definition of complex errors, even if the mapping is successful, but also to return to nil.
    – Method 7: successfully returns model object.

Before explaining the code, it is necessary to understand some of the data held by JSONModel:

  • KClassPropertiesKey: (to save all the related object attribute information NSDictionary) {age = “@property primitive age (Setters = []); name =” @property NSString* name (Standard JSON type, Setters = []); gender = “@property NSString* gender (Standard JSON type, Setters = []);}
  • The associated object kClassRequiredPropertyNamesKey: (the name NSSet used to hold all attributes) {(name, age, gender)}
  • Association object kMapperObjectKey: (used to save JSONKeyMapper): Custom mapper, the specific use of the above example can be seen.
  • JSONModelClassProperty: a property package jsonmodel, which contains the corresponding attribute name (name:gender), type (type:NSString), is a type of JSONModel support (isStandardJSONType:YES/NO), is a mutable object (isMutable:YES/NO) etc..

To explain the whole process roughly:
first, is initialized when the object model class, its traversal to all superclasses (until JSONModel), access to all attributes, and save it in a dictionary. Gets all the key from the incoming dictionary, matching the key with all the attributes that are saved. If the match is successful, the KVC assignment is performed.

OK, now from top to bottom gradually explain the upper part of the code:

First, in the load method, the type that the framework supports:

(void + load) {static dispatch_once_t once; dispatch_once (& once, @autoreleasepool ^{{/ / compatible object attribute 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]; / / compatible the basic type of attribute allowedPrimitiveTypes = @[@ @ "BOOL", "float", "int", @ @ @ "long", "double", "short", //and some @ famous aliases @ NSInteger, @ @ "NSUInteger", "Block"]; ValueTransformer = [[JSONValueTransformer alloc] / init] converter; / / JSONModelClass = NSClassFromString (their type NSStringFromClass (self));}}});

Then let’s look at what the author has done since the init method 3:

- (ID) init {self = [super init]; if (self) {[self __setup__]}; return self;} - (void __setup__) {/ / only is first instantiated, before the implementation of if (! Objc_getAssociatedObject (self.class, & kClassPropertiesKey) {[self __inspectProperties]}); / / if there is a custom mapper. It will be stored in the associated object inside, key is kMapperObjectKey ID mapper = [[self class] keyMapper] if (mapper; & & objc_getAssociatedObject! (self.class, & kMapperObjectKey)) {objc_setAssociatedObject (self.class, & kMapperObjectKey, mapper, OBJC_ASSOCIATION_ RETAIN / / This is atomic}});

It is worth noting that the __inspectProperties: method is one of the core methods of the framework: its task is to preserve all the attributes that need to be assigned. Used as a mapping in the future:

- (void __inspectProperties) {/ / ultimately save all properties of the dictionary form: / / {/ / age = "@property primitive age (Setters = []); / / friends =" @property NSArray*< Friend> friends (Standard JSON type, Setters = []); / / gender = "@property NSString* gender (Standard JSON type Setters = []"); / / name = "@property NSString* name (Standard JSON type, Setters = []); / / NSMutableDictionary* propertyIndex [NSMutableDictionary} = dictionary]; / / get the current class name Class class = [self class]; NSScanner* scanner NSString* = nil; propertyType = nil; / / condition: when class is JSONModel your termination while (class! = [JSONModel class]) {/ / attribute The number of unsigned int propertyCount; / / get the attribute list (all attributes declared in the @property objc_property_t *properties (class) = class_copyPropertyList, & propertyCount); / / traversal of all the attribute for (unsigned int i = 0; I < propertyCount; i++) {/ / objc_property_t property access to the property name = properties[i]; / / get the current char *propertyName = property_getName const attribute (property); //name (string C) each attribute in the //JSONModel, have been packaged into a JSONModelClassProperty object JSONModelClassProperty* P [[JSONModelClassProperty = alloc] init]; p.name = @ (propertyName); the //propertyName: attribute name, for example: n Ame, age, gender / char = *attrs for attribute of type const property_getAttributes (property); NSString* propertyAttributes = @ (attrs); / / T@/ NSString/, C, N, N, V_name / Tq, V_age / T@/ NSString/, C, N, V_gender / T@ "NSArray< Friend>, &" N, V_friends, NSArray*; attributeItems = [propertyAttributes componentsSeparatedByString:@ "]; / / that is," the read-only attribute, without any operation of the if ([attributeItems containsObject:@ "R"]) {continue}; / / to / / next property check out is a Boolean value of if ([propertyAttributes hasPrefix:@ "Tc"]) {p.structName = "@ BOOL"; / / make it To structure instantiate a scanner scanner} / / [NSScanner = scannerWithString: propertyAttributes]; [scanner scanUpToString:@ "T" intoString: [scanner scanString:@ "T" nil]; intoString:nil]; //http://blog.csdn.net/kmyhy/article/details/ 8258858 if ([scanner "intoString: scanString:@" @/ "& propertyType]) {/ / attribute is an object [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@" / "< intoString:&" propertyType]; propertyType; / / -> NSString p.type = NSClassFromString (propertyType); / / p.type = "@ NSString" P.isMutable ([propertyType rangeOfString:@ = "Mutable"].location! = NSNotFound); / / to determine whether the variable object p.isStandardJSONType [allowedJSONTypes = containsObject:p.type]; / / / / this type is compatible with the existence of a framework agreement (array, is the nested model while ([scanner) scanString:@ "< intoString:NULL]) {NSString* protocolName = nil [scanner scanUpToString:@;" > intoString: & protocolName]; if ([protocolName "Optional" isEqualToString:@ ") {p.isOptional = YES;} else (if [protocolName isEqualToString:@" Index "GCC diagnostic push") {#pragma #pragma GCC diagnostic ignored "-Wdeprecated-declarations" = YES #pragma GCC diagnostic p.isIndex; pop objc_setAssociatedObject (self.class, & kIndexPropertyNameKey, p.name, OBJC_ASSOCIATION_RETAIN / / This is atomic else if ([protocolName);} isEqualToString:@ "Ignore"]) {P} {p.protocol = nil; else = protocolName;} / / to the nearest > so far; [scanner scanString:@ "> intoString:NULL] else if;}} ([scanner scanString:@ {" intoString: & propertyType]) {/ / [scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet] attribute is the structure of intoString:& propertyType]; p.isStandardJSONType = NO; p.structName = propertyType;} else {/ / attribute is the basic types: Tq, N, V_age [scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@ "," intoString:& propertyType; //propert YType:q propertyType = valueTransformer.primitivesNames[propertyType]; //propertyType:long / if basic types of array ([allowedPrimitiveTypes! ContainsObject:propertyType]) {/ / @throw [NSException exceptionWithName:@ type does not support the "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 (optional [[self class] proper TyIsOptional:nsPropertyName]) {p.isOptional = YES;} / / negligible if ([[self class] propertyIsIgnored:nsPropertyName]) {P = nil;} / / customClass = [[self class] Class set classForCollectionProperty:nsPropertyName]; if (customClass) {p.protocol = NSStringFromClass (customClass);} / / if ([propertyType isEqualToString:@ ignore block "Block"]) {P = nil;} / / if the dictionary does not exist, is added to the attribute dictionary (finally added...) If (P & & [propertyIndex! ObjectForKey:p.name]) {[propertyIndex setValue:p forKey:p.name]}; //setter and getter if (P) {//name -> Name NSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange (0, 1) withString:[p.name substringToIndex:1].uppercaseString] SEL getter; / / getter = NSSelectorFromString ([NSString stringWithFormat:@ JSONObjectFor%@, name]); if ([self respondsToSelector:getter] p.customGetter) = getter; / / setters p.customSetters = [NSMutableDictionary new]; SEL genericSetter = NSSelectorFromString ([NSSt Ring 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 valueWithByt Es:& setter objCType:@encode (SEL)];}}} free (properties); / / point to their parent class, that is equal to JSONModel to stop the class [class = superclass];} / / last save all current, JSONModel all of the parent class attribute objc_setAssociatedObject (self.class, & kClassPropertiesKey, [propertyIndex copy], OBJC_ASSOCIATION_RETAIN)};

It is important to note that the author uses a while function to get the properties of the current class and the current class except all the parent classes of JSONModel in a dictionary. In the future for the introduction of the dictionary and mapping. The author encapsulates every attribute of JSONModel with JSONModelClassProperty class. This class has two important properties: one is name, and it is the name of the property (e.g. gender). The other is type, which is the type of attribute (e.g. NSString). The author divides attributes into the following types: objects (not including protocols). Object (including protocols, nested in the model). Basic data type. Structure body.

Let’s look at the implementation of method 4:

- (BOOL) __doesDictionary: (NSDictionary*) Dict matchModelWithKeyMapper: (JSONKeyMapper*) keyMapper error: (NSError** ERR) {/ / get the dictionary all key NSArray* incomingKeysArray = [dict allKeys]; / / array return save all attribute names (name, age, gender...) NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy; NSSet* incomingKeys = [NSSet / set to setWithArray: incomingKeysArray] from array; / / if user mapper, then convert if (keyMapper || globalKeyMapper) {NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count]; NSString* transformedName = nil; / / attribute list convenient to convert for (JSONM OdelClassProperty* property in [self __properties__]) {/ / is converted into the property name gender (-> model); sex (Dictionary) transformedName = (keyMapper||globalKeyMapper) [self __mapString:property.name? WithKeyMapper:keyMapper]: property.name; / / get the sex and view the incoming dictionary if there is a sex value corresponding to the ID value @try {value = [dict; valueForKeyPath:transformedName];} @catch (NSException *exception) {value = dict[transformedName];} / / if the value exists, sex will be added to the keys array passed in if (value) {[transformedIncomingKeys addObject: property.n Ame] incomingKeys = transformedIncomingKeys;}};} / / attribute attribute set to view the current model is greater than the incoming set, if it is, it returns an error. That is to say / attribute model class is no more than the incoming key dictionary, for example: if ([requiredProperties! IsSubsetOfSet:incomingKeys]) {/ / get more out of the //not all required attribute [requiredProperties minusSet:incomingKeys]; properties are in - invalid input JMLog ("Incoming data was invalid [%@ @ initWithDictionary:]. Keys missing:, self.class, requiredProperties% @"); if (ERR) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties]; return NO;} / / don't need to release the incomingKeys= nil; requiredProperties= nil; return YES;}

It is important to note that the attribute set defined in the model class is not larger than the key collection in the incoming dictionary. If there is a user-defined mapper, the user needs to be defined to convert.
(here is the award gender conversion for sex).

Finally, look at the framework of the second core code (the above method 5), that is, really get the value from the dictionary and assigned to the current model object:

- (BOOL) __importDictionary: (NSDictionary*) Dict withKeyMapper: (JSONKeyMapper*) keyMapper validation: (BOOL) validation error: (NSError** ERR) {/ / dictionary of all the attributes (JSONModelClassProperty* property traversal saved for in [self __properties__]) {/ / the name of the property to take over as key, use the key to find in the dictionary of correspondence the value of NSString* = jsonKeyPath (keyMapper||globalKeyMapper) [self __mapString:property.name? WithKeyMapper:keyMapper]: property.name; / / get used to save from the dictionary values ID jsonValue @try {jsonValue = [dict; valueForKeyPath: jsonKeyPath];} @catch (NSException *exception) {jsonValue = dict[jsonKeyPath];} The dictionary does not exist corresponding to the key / if (isNull (jsonValue)) {/ / if this key is not present in if (property.isOptional validation continue ||!); / / if this key is a must have, return an error if (ERR) {NSString* MSG = [NSString stringWithFormat:@ Value of required model key is% @ null JSONModelError*, property.name]; dataErr = [JSONModelError errorInvalidDataWithMessage:msg] = [dataErr errorByPrependingKeyPathComponent:property.name]; *err return NO;}}; / / get access to the value of the type Class jsonValueClass class] BOOL = [jsonValue; isValueOfAllowedType = NO; / / Check whether the frame is compatible with the attribute of type for (Class allowedType in allowedJSONTypes if ([jsonValueClass) {isSubclassOfClass: allowedType]) {isValueOfAllowedType = YES; break;}} / / if not compatible, it returns NO, the mapping if failed (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;};} / / if it is compatible with the type of if (property) {/ / check to see if there is a custom setter, and set the if ([self __customSetValue:jsonValue forProperty:property]) {continue;}; / / if basic types (property.type = = nil & & property.structName==nil) {//kvc (jsonValue = [self = if valueForKey:property.name]! [self setValue:jsonValue forKey: property.name]) {continue};}; / / if to value is empty, the corresponding attribute value of the current Is not empty, but also to a null value is assigned to it (if isNull (jsonValue) if ([self) {valueForKey:property.name]! = Nil) {[self} setValue:nil forKey: property.name]; continue;} / / the 1 attribute itself whether the jsonmodel type if ([self __isJSONModelSubClass:property.type]) {/ / through their own transfer model, obtain the corresponding value of JSONModelError* initErr = nil; id = value [[property.type alloc] initWithDictionary: jsonValue error:& initErr]; if (value!) {/ / if the property is not required, skip the if (property.isOptional Validati ||! On continue); / / if the attribute is required, an error is returned (if (ERR! = Nil) & & (initErr! = Nil) *err = [initErr) {errorByPrependingKeyPathComponent:property.name]}; return NO;} / / the current property value is empty, then the assignment of if ([value isEqual:[self valueForKey:property.name]] [self {!) setValue:value forKey: property.name] continue;};} else {/ / if it is not the type of jsonmodel, there may be some common types: NSArray, NSString... Whether the model (with / / nested protocol) if (property.protocol) {/ / into an array, the array is the friends attribute in the example. 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;}} / / object type if (property.isStandardJSONType & & [jsonValue; isKi NdOfClass: property.type]) {/ / variable type if (property.isMutable) {jsonValue [jsonValue = mutableCopy];} / / if ([jsonValue isEqual:[self valueForKey:property.name]] assignment!) {[self setValue:jsonValue forKey: property.name] continue;};} / / current value corresponding to the type and the type of the property is not the same as when, need to check whether the user defined (for example from converter NSSet to NSArray conversion: - (NSSet *) NSSetFromNSArray: (* NSArray) array (if) ([jsonValue isKindOfClass: property.type]! & & isNull! (jsonValue)) //the property is mutable property.isMutable || || //custom struct property property.structName around the) {/ / searched 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 (type: [%@] @ to 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); / / view custom converter whether there is BOOL foundCustomTransformer = NO; if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer} else {//try = YES; for = hidden custom transformer selectorName [NSString stringWithFor Mat:@ "__%@" selectorName]; selector = NSSelectorFromString (selectorName); if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer = YES;}} / / if there is a custom converter, conversion of if (foundCustomTransformer) {IMP imp = [valueTransformer methodForSelector:selector]; ID (*func) (ID, SEL, ID) = (void * IMP); jsonValue = func (valueTransformer, selector, jsonValue); if ([jsonValue isEqual:[self valueForKey:property.name]] [self s!) EtValue:jsonValue forKey:property.name];} else {/ / no custom converter, NSString* MSG = [NSString stringWithFormat:@ returns error "type not for%@.%@% @ supported, property.type, [self class], property.name] JSONModelError* dataErr; errorInvalidDataWithTypeMismatch:msg] = [JSONModelError; *err = [dataErr errorByPrependingKeyPathComponent:property.name]; return NO;}} else {/ / 3.4) handle" all other cases "(if any (if) [jsonValue isEqual:[self valueForKey: property.name]]! [self setValue:jsonV) Alue forKey:property.name];}}} return YES;}

It is worth noting that the author uses the KVC setValue:ForKey: method in the final assignment of attributes. The author determines whether the type of attribute in the model is a subclass of JSONModel, so that the author’s consideration is very comprehensive. The whole frame look down, there are many places related to the wrong judgment, the author will separate out a kind of wrong type (JSONModelError), which supports many types of errors, the author thought the side reaction can be careful. And it can also be used in our own framework or project.

An example of error judgment:

//JSONModelError.m + (ID) errorInvalidDataWithMessage: (NSString*) message [NSString stringWithFormat:@ {message = "Invalid JSON data:, return [JSONModelError% @" message]; errorWithDomain:JSONModelErrorDomain code:kJSONModelErrorInvalidData userInfo:@{NSLocalizedDescriptionKey:message}];}

Praised the author so much, the only place I personally do not like is that the if statement is only one line, the author does not like to add braces:

If (([jsonValue isEqual:[self valueForKey:property.name]]) [self setValue:jsonValue forKey:property.name]);

But I think it should be:

If (([jsonValue isEqual:[self valueForKey:property.name]]) {[self setValue:jsonValue forKey:property.name]};}

Knowledge expansion

  • The author uses the NSScanner to scan the string, the attributes from the class structure to take over the attributes of the description string T@/ “NSString/”, C, N, V_name scan of the type: NSString.
  • The author uses the two NSSet: when the order of elements in the collection is not important, give priority to using NSSet.

Overall, the difficulty of this framework is not large, but probably because it is not the first time to read the framework of UIVIiew, feeling a bit boring, but slowly get used to it!

This article has been synchronized to my personal blog: JSONModel source code analysis

Welcome to visit.


This article has been printed on the copyright, for reprint please visit the copyright print. Forty-eight million four hundred and twenty-two thousand nine hundred and twenty-eight

Obtain authorization