YYModel 源码解读(二)之NSObject+YYModel.h (4)
接下来我们继续向下看
typedef struct { void *modelMeta; ///< _YYModelMeta void *model; ///< id (self) void *dictionary; ///< NSDictionary (json) } ModelSetContext;
这是一个c的结构体,在c中 void * 相当于 oc 中的 id 类型
那么 为什么使用c的结构体呢,最主要的使用场景就是我们需要同时使用多个参数的情况下,可以使用c的结构体
/** Apply function for dictionary, to set the key-value pair to model. @param _key should not be nil, NSString. @param _value should not be nil. @param _context _context.modelMeta and _context.model should not be nil. */ static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) { ModelSetContext *context = _context; __unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta); __unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)]; __unsafe_unretained id model = (__bridge id)(context->model); while (propertyMeta) { if (propertyMeta->_setter) { ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta); } propertyMeta = propertyMeta->_next; }; }
上边的代码的主要作用是 根据 一个 id 类型的_value 一个 id 类型的_key 和_context 结构体信息,社会value 到相应的属性中
/** Apply function for model property meta, to set dictionary to model. @param _propertyMeta should not be nil, _YYModelPropertyMeta. @param _context _context.model and _context.dictionary should not be nil. */ static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) { ModelSetContext *context = _context; // 这个dictionary 是 json 字典 __unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary); __unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta); if (!propertyMeta->_setter) return; id value = nil; // 如果property 映射了 多个jsonkey if (propertyMeta->_mappedToKeyArray) { // 这个是之前的函数,目的是根据字典和映射的jsonkey 取出对应的值,当获取到第一个不为空的值的情况下,停止遍历 value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray); } else if (propertyMeta->_mappedToKeyPath) { value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath); } else { value = [dictionary objectForKey:propertyMeta->_mappedToKey]; } if (value) { __unsafe_unretained id model = (__bridge id)(context->model); ModelSetValueForProperty(model, value, propertyMeta); } }
上边的代码也是一个赋值的函数,在一种propertyMeta 和context 的参数的情况下 ,实现的赋值方法
接下来的这个方法主要是mode to json 模型转字典的核心方法,目的是对model 进行预处理
注意,该方法只是对一个NSObject对象做预处理,并没有转JSON对象 苹果 规定使用 NSJSONSerialization 转换成JSONObject的要求: NSArray 对象 数组元素只能是 NSString、NSNumber、NSNull NSDictionary 对象 key 必须是 NSString value只能是 NSString、NSNumber、NSNull 该函数对传入的NSObject对象为如下所有类型时做的预处理: NSData >>> 不能转换成JSON NSString >>> NSString NSNumber >>> NSNumber NSURL >>> NSString NSAttributedString >>> NSString NSDate >>> 使用DateFormat转换成NSString NSArray 先判断NSArray是否可以被JSON化 如果可以,直接返回NSArray 如果不可以 创建一个新的NSArray 遍历之前NSArray中的每一个元素 递归将当前元素解析成 NSString、NSNumber 将解析后的元素添加到数组 返回新的数组 NSSet NSSet >>> NSArray 走NSArray的流程 NSDictionary 类似NSArray 自定义NSObject类,非Foundation类 将当前实体类对象 >>> 使用NSDictionary对象来重新组装 属性值 >>> NSDictionary字典key-value 最终 实体类对象 >>> NSDictionary对象
/** Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull), or nil if an error occurs. @param model Model, can be nil. @return JSON object, nil if an error occurs. */ /** * 说明该方法主要使用在模型转字典的功能中 * * @param model 模型 * * @return 与转换后的值,因为有些值是需要处理的 */ static id ModelToJSONObjectRecursive(NSObject *model) { // 1.kCFNull 或者 Null if (!model || model == (id)kCFNull) return model; // 2.NSString 支持转json 直接返回该值 if ([model isKindOfClass:[NSString class]]) return model; // 3.NSNumber 支持转json 直接返回该值 if ([model isKindOfClass:[NSNumber class]]) return model; // 4.NSDictionary value 必须是NSString、NSNumber、NSNull key 必须是NSString 才支持转json 需要进行判断 if ([model isKindOfClass:[NSDictionary class]]) { // 4.1 调用NSJSONSerialization 的 isValidJSONObject: 方法 检测能否直接转json if ([NSJSONSerialization isValidJSONObject:model]) return model; // 4.2 不能直接转json的情况,这个时候需要新建一个可变字典对象,转换成功后转换该字典 NSMutableDictionary *newDic = [NSMutableDictionary new]; [((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { // 4.2.1 对key的处理,NSDictionary的key 可以自定义,因此还有这种额外的情况 NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description; if (!stringKey) return; // 4.2.2 通过递归的方式(调用自身) 预处理 字典中的value id jsonObj = ModelToJSONObjectRecursive(obj); if (!jsonObj) jsonObj = (id)kCFNull; // 4.2.3 使用处理过的 key value 给字典赋值 newDic[stringKey] = jsonObj; }]; return newDic; } // 5.集合,数组转json 跟字典一样 都要求是NSString、NSNumber、NSNull if ([model isKindOfClass:[NSSet class]]) { // 5.1 NSSet 转换成NSArray NSArray *array = ((NSSet *)model).allObjects; // 5.2 如果能直接转,直接返回就行了 if ([NSJSONSerialization isValidJSONObject:array]) return array; // 5.3 这里是不能直接转的情况 NSMutableArray *newArray = [NSMutableArray new]; for (id obj in array) { // 5.3.1 如果是NSString 或者 NSNumber 类型,可以直接转换 if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) { [newArray addObject:obj]; } // 5.3.2 其他的 调用自身 预处理 else { id jsonObj = ModelToJSONObjectRecursive(obj); if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj]; } } return newArray; } // 6. 同上 5 再次不做多余的解释 if ([model isKindOfClass:[NSArray class]]) { if ([NSJSONSerialization isValidJSONObject:model]) return model; NSMutableArray *newArray = [NSMutableArray new]; for (id obj in (NSArray *)model) { if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) { [newArray addObject:obj]; } else { id jsonObj = ModelToJSONObjectRecursive(obj); if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj]; } } return newArray; } // 7. 对 NSURL 转成NSString if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString; // 8. 对NSAttributedString 转成NSString if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string; // 9. 对NSDate 转成NSString if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model]; // 10. 对NSData 不做处理,直接返回nil if ([model isKindOfClass:[NSData class]]) return nil; // 11. 如果不是上边的类型,就说明使用了自定义的类型。需要把自定义的类型转成NSDictionary 处理 // 11.1 根据类型Model 初始化一个_YYModelMeta _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]]; if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil; // 11.2 新建一个数组,使用__unsafe_unretained 来避免 在block 中的 retain 和 release NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64]; __unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block // 11.3 遍历modelMeta->_mapper 这个字典 得到 jsonkey 和 _YYModelPropertyMeta描述 [modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) { // 11.3.1 判空 if (!propertyMeta->_getter) return; // 11.3.2 value 用来接收转换后的值 id value = nil; // 11.3.3 如果是_isCNumber if (propertyMeta->_isCNumber) { // 11.3.3.1 通过调用ModelCreateNumberFromProperty 函数 获得转换后的NSNumber值 value = ModelCreateNumberFromProperty(model, propertyMeta); } // 11.3.4 如果是_nsType 类型 else if (propertyMeta->_nsType) { // 11.3.4.1 转换 id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); value = ModelToJSONObjectRecursive(v); } // 11.3.5 其他的情况 else { // 11.3.5.1 判断propertyMeta->_type 根据不同的类型转换成NSString 类型 switch (propertyMeta->_type & YYEncodingTypeMask) { case YYEncodingTypeObject: { id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); value = ModelToJSONObjectRecursive(v); if (value == (id)kCFNull) value = nil; } break; case YYEncodingTypeClass: { Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); value = v ? NSStringFromClass(v) : nil; } break; case YYEncodingTypeSEL: { SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter); value = v ? NSStringFromSelector(v) : nil; } break; default: break; } } if (!value) return; // 到此 我们 已经获取到了 转换成功后的 value 了 // 11.3.6 处理keypath 的情况 /** * 将一个实体类对象属性值,按照json keypath,转换成对应的字典 * @"AA" --映射-- @"user.name" * * 还原成 * * @{ @"id" : 20 , * @"user" : @{ * @"name" : @"AA", * } * } */ if (propertyMeta->_mappedToKeyPath) { NSMutableDictionary *superDic = dic; NSMutableDictionary *subDic = nil; // 便利keypath数组 上边的例子中 [@"user",@"name"] for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) { // 11.3.6.1 取出数组中国的key NSString *key = propertyMeta->_mappedToKeyPath[i]; // 11.3.6.2 如果便利到最后一个的时候,对 if (i + 1 == max) { // end if (!superDic[key]) superDic[key] = value; break; } // 11.3.6.3 给最上边的父类 赋值 上边的例子中dic[@"user"] == subDic subDic = superDic[key]; if (subDic) { if ([subDic isKindOfClass:[NSDictionary class]]) { subDic = subDic.mutableCopy; superDic[key] = subDic; } else { break; } } else { subDic = [NSMutableDictionary new]; superDic[key] = subDic; } superDic = subDic; subDic = nil; /* 总之,上边这个循环的处理目的就是给字典中keypath 映射的模型中的属性赋值 如果 映射的模型中的该属性有值就不赋值了 */ } } else { if (!dic[propertyMeta->_mappedToKey]) { dic[propertyMeta->_mappedToKey] = value; } } }]; // 11.4 判断该类是否实现了modelCustomTransformToDictionary 方法 if (modelMeta->_hasCustomTransformToDictionary) { BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic]; if (!suc) return nil; } return result; }
下边的代码是控制行的缩进
NSMutableString *str = 可变的@"xxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxx\nxxxxxxxxxxxxxxxxxxxxxxxx";
显示是这样的
xxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx
ModelDescriptionAddIndent(str,1) 的结果就是这样的
xxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxx
/// Add indent to string (exclude first line) // 这个函数的目的就是除了第一行,其他的行都缩进 indent * 4 个 字符 static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) { for (NSUInteger i = 0, max = desc.length; i < max; i++) { unichar c = [desc characterAtIndex:i]; if (c == '\n') { for (NSUInteger j = 0; j < indent; j++) { [desc insertString:@" " atIndex:i + 1]; } i += indent * 4; max += indent * 4; } } return desc; }
类似系统的获取对这个模型的描述
/// Generate a description string static NSString *ModelDescription(NSObject *model) { // 1. 设置描述的最大长度为100 static const int kDescMaxLength = 100; // 2. 进行判空或者不是NSObject的处理 if (!model) return @"<nil>"; if (model == (id)kCFNull) return @"<null>"; if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model]; // 3. 获取modelMeta的抽象类 _YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class]; // 3.1 使用抽象类的类型 switch (modelMeta->_nsType) { // 3.1.1 字符串 case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: { return [NSString stringWithFormat:@"\"%@\"",model]; } // 3.1.2 NSVale 或者NSData case YYEncodingTypeNSValue: case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: { NSString *tmp = model.description; // 处理超过最大描述长度 if (tmp.length > kDescMaxLength) { tmp = [tmp substringToIndex:kDescMaxLength]; tmp = [tmp stringByAppendingString:@"..."]; } return tmp; } // 3.1.3 NSNumber NSDate NSURL case YYEncodingTypeNSNumber: case YYEncodingTypeNSDecimalNumber: case YYEncodingTypeNSDate: case YYEncodingTypeNSURL: { return [NSString stringWithFormat:@"%@",model]; } // 3.1.4 NSSet case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: { model = ((NSSet *)model).allObjects; } // no break // 3.1.5 NSArray case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: { NSArray *array = (id)model; // 便利数组,逐行打印数组内部的类型 NSMutableString *desc = [NSMutableString new]; if (array.count == 0) { return [desc stringByAppendingString:@"[]"]; } else { [desc appendFormat:@"[\n"]; for (NSUInteger i = 0, max = array.count; i < max; i++) { NSObject *obj = array[i]; [desc appendString:@" "]; [desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)]; [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; } [desc appendString:@"]"]; return desc; } } // 3.1.6 NSDictionary case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: { NSDictionary *dic = (id)model; // 便利字典,逐行打印字典的key value NSMutableString *desc = [NSMutableString new]; if (dic.count == 0) { return [desc stringByAppendingString:@"{}"]; } else { NSArray *keys = dic.allKeys; [desc appendFormat:@"{\n"]; for (NSUInteger i = 0, max = keys.count; i < max; i++) { NSString *key = keys[i]; NSObject *value = dic[key]; [desc appendString:@" "]; [desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)]; [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; } [desc appendString:@"}"]; } return desc; } // 3.1.7 自定义的类型 default: { // 如果没有属性,则打印内存地址 NSMutableString *desc = [NSMutableString new]; [desc appendFormat:@"<%@: %p>", model.class, model]; if (modelMeta->_allPropertyMetas.count == 0) return desc; // sort property names 按名称排序 NSArray *properties = [modelMeta->_allPropertyMetas sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) { return [p1->_name compare:p2->_name]; }]; [desc appendFormat:@" {\n"]; for (NSUInteger i = 0, max = properties.count; i < max; i++) { _YYModelPropertyMeta *property = properties[i]; NSString *propertyDesc; if (property->_isCNumber) { NSNumber *num = ModelCreateNumberFromProperty(model, property); propertyDesc = num.stringValue; } else { switch (property->_type & YYEncodingTypeMask) { case YYEncodingTypeObject: { id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); propertyDesc = ModelDescription(v); if (!propertyDesc) propertyDesc = @"<nil>"; } break; case YYEncodingTypeClass: { id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); propertyDesc = ((NSObject *)v).description; if (!propertyDesc) propertyDesc = @"<nil>"; } break; case YYEncodingTypeSEL: { SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); if (sel) propertyDesc = NSStringFromSelector(sel); else propertyDesc = @"<NULL>"; } break; case YYEncodingTypeBlock: { id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); propertyDesc = block ? ((NSObject *)block).description : @"<nil>"; } break; case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: { void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter); propertyDesc = [NSString stringWithFormat:@"%p",pointer]; } break; case YYEncodingTypeStruct: case YYEncodingTypeUnion: { NSValue *value = [model valueForKey:property->_name]; propertyDesc = value ? value.description : @"{unknown}"; } break; default: propertyDesc = @"<unknown>"; } } propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1); [desc appendFormat:@" %@ = %@",property->_name, propertyDesc]; [desc appendString:(i + 1 == max) ? @"\n" : @";\n"]; } [desc appendFormat:@"}"]; return desc; } } }