1、json中有[NSNull null]类型 — JSONModel和YYModel都有适配
2、异常情况:NSString <-> NSNumber — JSONModel和YYModel都有适配
3、异常情况:NSString <-> NSUInteger —JSONModel没有适配,会crash,YYModel有适配
4、异常情况:NSArray <-> NSString —JSONModel和YYModel都有没有适配
5、NSCoding 协议(持久化)的支持 —JSONModel和YYModel都有适配
6、是否可以嵌套Model —JSONModel和YYModel都有适配
7、NSArray中可以包含Model —JSONModel和YYModel都有适配
8、未知字段的适配(向后兼容) —JSONModel和YYModel都有适配
9、继承情况下对于多态的支持 —JSONModel没有适配,YYModel适配了
注意:好像YYModel再gethub上面的更新时间停留在2年前,好像近期都没有更新了。
我们可以先看看核心代码
1、先是判断传入的字典是否为空,如果为空返回为空的错误
2、再判断传入的数据是否是字典类型,如果不是字典类型不正确的错误
3、核心的代码,通过init方法初始化映射property,(核心代码,等会再下面解释)
4、获取当前类的keyMapper
5、检查映射结构是否能从我们传入的dict中找到对应的数据,如果不能找到,就返回nil,并且抛出错误
6、根据传入的dict进行数据的赋值,如果赋值没有成功,就返回nil,并且抛出错误
7、根据本地的错误来判断是否有错误,如果有错误,就返回nil
8、返回self
-(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;
}
//create a class instance
//第三步:核心的代码,通过init方法初始化映射property
self = [self init];
if (!self) {
//super init didn't succeed
if (err) *err = [JSONModelError errorModelIsInvalid];
return nil;
}
//第四步:获取当前类的keyMapper
//key mapping
JSONKeyMapper* keyMapper = [self __keyMapper];
//第五步:检查映射结构是否能从我们传入的dict中找到对应的数据,如果不能找到,就返回nil,并且抛出错误
//check incoming data structure
if (![self __doesDictionary:dict matchModelWithKeyMapper:keyMapper error:err]) {
return nil;
}
//import the data from a dictionary
//第六步:根据传入的dict进行数据的赋值,如果赋值没有成功,就返回nil,并且抛出错误。
if (![self __importDictionary:dict withKeyMapper:keyMapper validation:YES error:err]) {
return nil;
}
//第七步:根据本地的错误来判断是否有错误,如果有错误,就返回nil,并且抛出错误。
//run any custom model validation
if (![self validate:err]) {
return nil;
}
//第八步:返回self
//model is valid! yay!
return self;
}
第三步:核心代码的解析–通过init方法初始化映射property。
a、他会先调用init方法,init方法中点用了“setup“方法。–[self setup]。
-(id)init
{
self = [super init];
if (self) {
//do initial class setup
[self __setup__];
}
return self;
}
我们先解析一下“setup”方法
1、先是通过AssociateObject来判断是否进行过映射property的缓存,如果没有就使用“__inspectProperties”方法进行映射property的缓存–(后面我会解析一下“__inspectProperties”方法,介绍怎么进行映射property的缓存)
2、获取当前类的keyMapper映射。
3、判断一下,当前的keyMapper是否存在和是否进行映射过,如果没有进行映射就使用AssociateObject方法进行映射。
4、进行AssociateObject映射。
-(void)__setup__
{
//if first instance of this model, generate the property list
//第一步: 先是通过AssociateObject来判断是否进行过映射property的缓存,如果没有就使用“__inspectProperties”方法进行映射property的缓存
if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {
//进行映射property的缓存
[self __inspectProperties];
}
//if there's a custom key mapper, store it in the associated object
//第二步:获取keyMapper
id mapper = [[self class] keyMapper];
//第三步:判断一下,当前的keyMapper是否存在和是否进行映射过,如果没有进行映射就使用AssociateObject方法进行映射
if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {
//第四步:进行AssociateObject映射
objc_setAssociatedObject(
self.class,
&kMapperObjectKey,
mapper,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
}
解析一下“__inspectProperties”方法,介绍怎么进行映射property的缓存
注意:NSScanner是用于在字符串中扫描指定的字符,特别是把它们翻译/转换为数字和别的字符串。可以在创建NSScaner时指定它的string属性,然后scanner会按照你的要求从头到尾地扫描这个字符串的每个字符。
1、先是获取当前class的property列表和个数
2、然后再遍历这些property
3、把我们的property通过一个局部变量进行赋值–JSONModelClassProperty,这个是JSONModel提供的类,来解析每个property。
4、获取property的名称给当前这个局部变量
5、获取这个property的属性
6、判断这个property的属性值里面是否包含”Tc,”,如果包含就设置structName为BOOL
7、扫描property属性
8、设置property的类型
9、判断并设置property的是否是可变的
10、判断property的是否我们允许的json类型
11、解析protocol的string
12、检查property是否为structure
13、判断property是不是Optional
14、判断property是不是Ignored
15、判断property是不是只读属性
16、通过kvc去设置相应的值
17、使用AssociateObject进行缓存
-(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));
//第一步:先是获取当前class的property列表和个数
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
//第二步:遍历property
//loop over the class properties
for (unsigned int i = 0; i < propertyCount; i++) {
//第三步:创建一个解析和判断每个property的局部变量JSONModelClassProperty
JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
//get property name
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
//第四步:获取property的名称给当前这个局部变量
p.name = @(propertyName);
//JMLog(@"property: %@", p.name);
//get property attributes
//第五步:获取这个property的属性
const char *attrs = property_getAttributes(property);
NSString* propertyAttributes = @(attrs);
//第六步:判断这个property的属性值里面是否包含"Tc,",如果包含就设置structName为BOOL
if ([propertyAttributes hasPrefix:@"Tc,"]) {
//mask BOOLs as structs so they can have custom convertors
p.structName = @"BOOL";
}
//第七步:扫描property属性
scanner = [NSScanner scannerWithString: propertyAttributes];
//JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
[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]) {
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
intoString:&propertyType];
//JMLog(@"type: %@", propertyClassName);
//第八步:设置property的类型
p.type = NSClassFromString(propertyType);
//第九步:判断并设置property的是否是可变的
p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);
//第十步:判断property的是否我们允许的json类型
p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];
//read through the property protocols
//第十一步:解析protocol的string
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 if ([self propertyIsReadOnly:p.name]) {
p = nil;
} else {
p.protocol = protocolName;
}
[scanner scanString:@">" intoString:NULL];
}
}
//check if the property is a structure
//第十二步:检查property是否为structure
else if ([scanner scanString:@"{" intoString: &propertyType]) {
[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
intoString:&propertyType];
p.isStandardJSONType = NO;
p.structName = propertyType;
}
//the property must be a primitive
else {
//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];
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);
//第十三步:判断property是不是Optional
if([[self class] propertyIsOptional:nsPropertyName]){
p.isOptional = YES;
}
//第十四步:判断property是不是Ignored
if([[self class] propertyIsIgnored:nsPropertyName]){
p = nil;
}
//第十五步:判断property是不是只读属性
if([self propertyIsReadOnly:nsPropertyName]) {
p = nil;
}
//add the property object to the temp index
//第十六步:通过kvc去设置相应的值
if (p) {
[propertyIndex setValue:p forKey:p.name];
}
}
free(properties);
//ascend to the super of the class
//(will do that until it reaches the root class - JSONModel)
class = [class superclass];
}
//finally store the property index in the static property index
//第十七步:使用AssociateObject进行缓存
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
在解析一下“-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err”查值的代码。
1、检查一下所有的必要属性都存在,并且把他们都放入set中
2、判断是否存在keyMapper映射,如果存在,在对应的set中找到对应的key进行替换
3、判断那些必须的Propertie的集合是否全部都包括
-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{
//check if all required properties are present
//第一步:检查一下所有的必要属性都存在,并且把他们都放入set中
NSArray* incomingKeysArray = [dict allKeys];
NSMutableSet* requiredProperties = [self __requiredPropertyNames];
NSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];
//transform the key names, if neccessary
//第二步:判断是否存在keyMapper映射,如果存在,在对应的set中找到对应的key进行替换
if (keyMapper) {
NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];
NSString* transformedName = nil;
//loop over the required properties list
//这个是遍历所需要的属性列表
for (JSONModelClassProperty* property in [self __properties__]) {
//get the mapped key path
transformedName =
//获取mapped的关键值transformedName
keyMapper.modelToJSONKeyBlock(property.name);
//chek if exists and if so, add to incoming keys
//检查该值是否存在,如果存在,加入到incoming集合中
id value;
@try {
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
//第三步:判断那些必须的Properties的集合是否全部都包括
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.class, requiredProperties);
if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];
return NO;
}
//not needed anymore
incomingKeys= nil;
requiredProperties= nil;
return YES;
}
下面这个方法是判断哪些属性是必须的
-(NSMutableSet*)__requiredPropertyNames
{
//fetch the associated property names
NSMutableSet* classRequiredPropertyNames = objc_getAssociatedObject(self.class, &kClassRequiredPropertyNamesKey);
if (!classRequiredPropertyNames) {
classRequiredPropertyNames = [NSMutableSet set];
[[self __properties__] enumerateObjectsUsingBlock:^(JSONModelClassProperty* p, NSUInteger idx, BOOL *stop) {
if (!p.isOptional) [classRequiredPropertyNames addObject:p.name];
}];
//persist the list
objc_setAssociatedObject(
self.class,
&kClassRequiredPropertyNamesKey,
classRequiredPropertyNames,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
return classRequiredPropertyNames;
}
最后解析一下查找属性以后的赋值操作
1、循环遍历映射出来的JSONModelClassProperty
2、获取keyMapper的映射,获取真正的值
3、判断数据输入的类型是不是我们允许的json类型
4、检查model中是否有匹配的属性
5、检查自定义的setter,使用对应的set进行赋值—-(这个具体看代码里面的判断,我就不列举出来了)
6、处理一些转化类型,比如string <-> number、string <-> url
-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{
//loop over the incoming keys and set self's properties
//第一步:循环遍历映射出来的JSONModelClassProperty
for (JSONModelClassProperty* property in [self __properties__]) {
//convert key name ot model keys, if a mapper is provided
//第二步:获取keyMapper的映射,获取真正的值
NSString* jsonKeyPath = property.name;
if (keyMapper) jsonKeyPath = keyMapper.modelToJSONKeyBlock( property.name );
//JMLog(@"keyPath: %@", jsonKeyPath);
//general check for data type compliance
id jsonValue;
@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;
//第三步:判断数据输入的类型是不是我们允许的json类型
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
//第四步:检查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
//第五步:检查自定义的setter,使用对应的set进行赋值
if ([self __customSetValue:jsonValue forProperty:property]) {
//skip to next JSON key
continue;
};
// 0) handle primitives
//如果是基础类型,不如Bool等,我们直接使用kvc进行赋值
if (property.type == nil && property.structName==nil) {
//generic setter
[self setValue:jsonValue forKey: property.name];
//skip directly to the next key
continue;
}
// 0.5) handle nils
if (isNull(jsonValue)) {
[self setValue:nil forKey: property.name];
continue;
}
// 1) check if property is itself a JSONModel
//检查属性是不是JSONModel结构,进行遍历,把所有的结构遍历并且赋值
if ([self __isJSONModelSubClass:property.type]) {
//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;
}
[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
// 判断property中是否包含protocol的字段。(该字段主要用来表明array或者dictionary中的对象类型)
if (property.protocol) {
//JMLog(@"proto: %@", p.protocol);
//循环遍历子内容,再将对应的类型赋值给对应的dictionary或者array
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
//判断是否是标准的json类型
if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {
//mutable properties
//判断是否可变的类型
if (property.isMutable) {
jsonValue = [jsonValue mutableCopy];
}
//set the property value
//kvc赋值
[self setValue:jsonValue forKey: property.name];
continue;
}
// 3.3) handle values to transform
//第六步:处理一些转化类型,比如string <-> number、string <-> url
//做一些基础的判断,是否是对应的类型,是否为空,是否是可变类型,是否是自定义的结构
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)
//下面这句,就是获取真实的json数据的类型
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
//为属性和json对象类构建一个方法选择器
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
//使用 JSONValueTransformer 进行类型转换
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];
//解决api返回的数据类型,与jsonmodel中声明的数据类型不一致时的crash
return NO;
}
} else {
// 3.4) handle "all other" cases (if any)
//处理所有其他的case
[self setValue:jsonValue forKey: property.name];
}
}
}
}
return YES;
}
1、使用了runTime动态解析model数据类型
2、使用了AssociatedObject缓存
3、使用了NSScanner扫描字符串
4、使用了keyMapper映射
5、使用了KVC赋值
6、使用了JSONValueTransformer类型转换
1、命名自动匹配—-model的属性名称和服务器返回的一致,比如关键字id我们可以使用keyMapper了来映射成其他的属性名称。
2、model中可以关联其他的model,只要指定对应的自身的类型
3、model中可以集合其他的model集合,这样必须要实现@protocol协议
4、在一个Model中获取服务器返回数据不同层级的数据
5、可以设置全局键映射
6、可以设置下划线自动转化为驼峰
7、可以设置可选属性、忽略属性
8、设置所有属性为可选也表示可以所有可选属性为nil
9、可以使用内置的HTTP链接
10、自定义数据处理,内嵌转换比如类型之间的转换
11、可以自定义处理特殊的属性
12、可以自定义JSON的校验
我们只解析比较重要的几种特点
1、使用了runTime动态解析model数据类型
a、使用class_copyPropertyList方法去获取所有model对象的属性列表
b、使用property_getAttributes获取当前属性的property的encode string
c、使用property_getName去获取属性的name
2、使用了AssociatedObject缓存
AssociatedObject有三个相关的函数,我们的JSONModel主要使用的是objc_setAssociatedObject。
这个是objc_setAssociatedObject的调用的函数的过程
a、根据传入的value来获取new_value,判断new_value是否存在。
b、根据我们对象的地址获取对应的ObjectAssociationMap的地址,判断该对象是否存在。就根据传入的key获取对象的关联对象。
c、如果不存在,为该对象创建一个ObjectAssociationMap对象,存入新的关联对象,设置好标识位,再判断就的关联对象是否存在,如果存在释放掉旧的关联对象。
d、如果存在,持有旧的关联对象,存入新的关联对象。
e、再判断判断旧的关联对象是否存在,如果存在就释放掉旧的关联对象。
以下是源码
void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
// retain the new value (if any) outside the lock.
ObjcAssociation old_association(0, nil);
id new_value = value ? acquireValue(value, policy) : nil;
{
AssociationsManager manager;
AssociationsHashMap &associations(manager.associations());
disguised_ptr_t disguised_object = DISGUISE(object);
if (new_value) {
// break any existing association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
// secondary table exists
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
j->second = ObjcAssociation(policy, new_value);
} else {
(*refs)[key] = ObjcAssociation(policy, new_value);
}
} else {
// create the new association (first time).
ObjectAssociationMap *refs = new ObjectAssociationMap;
associations[disguised_object] = refs;
(*refs)[key] = ObjcAssociation(policy, new_value);
object->setHasAssociatedObjects();
}
} else {
// setting the association to nil breaks the association.
AssociationsHashMap::iterator i = associations.find(disguised_object);
if (i != associations.end()) {
ObjectAssociationMap *refs = i->second;
ObjectAssociationMap::iterator j = refs->find(key);
if (j != refs->end()) {
old_association = j->second;
refs->erase(j);
}
}
}
}
// release the old value (outside of the lock).
if (old_association.hasValue()) ReleaseValue()(old_association);
}
3、使用了NSScanner扫描字符串
NSScanner对象把NSString对象的的字符转化成 number和string类型。在创建NSScanner对象的时候为它分配字符,当你从NSScanner对象获取内容的时候,它会从头到尾遍历字符串。也可以通过charactersToBeSkipped方法来忽略某些字符。也可以通过[scanner scanLocation]来设置设置扫描开始的地方。还可以通过caseSensitive方法来区分字符串中大小写。
还可以通过一些方法来扫描一些特定类型的数据,比如:scanFloat—扫描单精度浮点型字符
scanHexDouble—扫描双精度的十六进制类型
scanHexInt —- 扫描十六进制无符整型等等方法
还有就是一些扫描特定字符的方法
scanCharactersFromSet:intoString—- 扫描字符串中和NSCharacterSet字符集中匹配的字符,是按字符单个匹配的
scanUpToCharactersFromSet:intoString —- 扫描字符串,直到碰到NSCharacterSet字符串的时候就停下来等等,具体的一些方法可以去看 NSScanner官方文档。
4、使用了JSONValueTransformer类型转换
目前提供的方法有以下这些:
NSMutableString <-> NSString
NSMutableArray <-> NSArray
NS(Mutable)Array <- JSONModelArray
NSMutableDictionary <-> NSDictionary
NSSet <-> NSArray
BOOL <-> number/string
string <-> number
string <-> url
string <-> time zone
string <-> date
number <-> date
https://blog.csdn.net/sodaslay/article/details/77870044
https://blog.csdn.net/u012496940/article/details/50032559
https://blog.csdn.net/game3108/article/details/52043661
http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/
https://www.jianshu.com/p/fbebd33d5b34
如果有写的不正确或者侵权的,希望大家给我提出来,我会及时修改。谢谢大家。