MJExtension 想必大家都不陌生, 但是MJExtension
是json
和model
之间相互转化的框架,在http协议中, 这种框架是非常方便的, 那么如何让模型转模型呢?
这个问题说起来大家可能很奇怪, 已经是模型了, 为什么还要转换, 相信使用过Protobuff
协议开发的都会做过类似的事情, 将PB
协议中的模型转换成自己项目中的模型, 这样模型一多, 就要写很多脑残的代码, 从模型转模型.
其实使用MJExtension
本身就可以从模型转模型, 那就是先把模型转字典, 再把字典转成模型. 但是这样一来效率就有问题了.
下面说下高效的转换方法,直接模型转模型的方法##
这个是MJ字典转模型的核心算法###
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
这个是MJ模型转字典的核心算法###
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys
这两个功能是相似的, 所以我们定义这样的方法来进行模型转模型
+ (instancetype)pb_object:(id)srcModel;
这里是类方法, 核心的算法是- (instancetype)pb_object:(id)srcModel ignoredKeys:(NSArray *)ignoredKeys
+ (instancetype)pb_object:(id)srcModel {
return [self pb_object:srcModel ignoredKeys:nil];
}
+ (instancetype)pb_object:(id)srcModel ignoredKeys:(NSArray *)ignoredKeys {
return [[[self alloc] init] pb_object:srcModel ignoredKeys:ignoredKeys];
}
- (instancetype)pb_object:(id)srcModel ignoredKeys:(NSArray *)ignoredKeys
下面就来看下- (instancetype)pb_object:(id)srcModel ignoredKeys:(NSArray *)ignoredKeys
- (instancetype)pb_object:(id)srcModel ignoredKeys:(NSArray *)ignoredKeys {
// 如果自己不是模型类, 那就返回自己
MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类")
// 取出源model中的所有属性
Class srcClazz = [srcModel class];
NSArray *allowedPropertyNames = [srcClazz mj_totalAllowedPropertyNames];
NSArray *ignoredPropertyNames = [srcClazz mj_totalIgnoredPropertyNames];
[srcClazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
@try {
// 0.检测是否被忽略
if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return;
if ([ignoredPropertyNames containsObject:property.name]) return;
// if (keys.count && ![keys containsObject:property.name]) return;
if ([ignoredKeys containsObject:property.name]) return;
// 1.取出属性值
id value = [property valueForObject:srcModel];
if (!value) return;
// 值的过滤
id newValue = [srcClazz mj_getNewValueFromObject:srcModel oldValue:value property:property];
if (newValue != value) { // 有过滤后的新值
[property setValue:newValue forObject:srcModel];
return;
}
// 如果没有值,就直接返回
if (!value || value == [NSNull null]) return;
// 2.复杂处理
Class dstClazz = [self class];
MJProperty *dstProperty = [dstClazz pb_propertyForPropertyName:property.name];
MJPropertyType *type = dstProperty.type;
Class propertyClass = type.typeClass;
Class objectClass = [dstProperty objectClassInArrayForClass:[self class]];
// 不可变 -> 可变处理
if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) {
value = [NSMutableArray arrayWithArray:value];
} else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) {
value = [NSMutableDictionary dictionaryWithDictionary:value];
} else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) {
value = [NSMutableString stringWithString:value];
} else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) {
value = [NSMutableData dataWithData:value];
}
if (!type.isFromFoundation && propertyClass) { // 模型属性
value = [propertyClass pb_object:value ignoredKeys:ignoredKeys];
} else if (objectClass) {
if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) {
// string array -> url array
NSMutableArray *urlArray = [NSMutableArray array];
for (NSString *string in value) {
if (![string isKindOfClass:[NSString class]]) continue;
[urlArray addObject:string.mj_url];
}
value = urlArray;
} else { // 字典数组-->模型数组
value = [objectClass mj_objectArrayWithKeyValuesArray:value context:nil];
}
} else {
if (propertyClass == [NSString class]) {
if ([value isKindOfClass:[NSNumber class]]) {
// NSNumber -> NSString
value = [value description];
} else if ([value isKindOfClass:[NSURL class]]) {
// NSURL -> NSString
value = [value absoluteString];
}
} else if ([value isKindOfClass:[NSString class]]) {
if (propertyClass == [NSURL class]) {
// NSString -> NSURL
// 字符串转码
value = [value mj_url];
} else if (type.isNumberType) {
NSString *oldValue = value;
// NSString -> NSNumber
if (type.typeClass == [NSDecimalNumber class]) {
value = [NSDecimalNumber decimalNumberWithString:oldValue];
} else {
value = [numberFormatter_ numberFromString:oldValue];
}
// 如果是BOOL
if (type.isBoolType) {
// 字符串转BOOL(字符串没有charValue方法)
// 系统会调用字符串的charValue转为BOOL类型
NSString *lower = [oldValue lowercaseString];
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
value = @YES;
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
value = @NO;
}
}
}
}
// value和property类型不匹配
if (propertyClass && ![value isKindOfClass:propertyClass]) {
value = nil;
}
}
// 3.赋值
[dstProperty setValue:value forObject:self];
} @catch (NSException *exception) {
MJExtensionBuildError([self class], exception.reason);
MJExtensionLog(@"%@", exception);
}
}];
return self;
}
代码有点长, 但是基本上主要的和字典转模型的不同是
Class dstClazz = [self class];
MJProperty *dstProperty = [dstClazz pb_propertyForPropertyName:property.name];
MJPropertyType *type = dstProperty.type;
Class propertyClass = type.typeClass;
Class objectClass = [dstProperty objectClassInArrayForClass:[self class]];
这里是通过源模型的属性名获取目标模型的属性
+ (MJProperty *)pb_propertyForPropertyName:(NSString *)oldName {
NSArray *cachedProperties = [self properties];
for (MJProperty *property in cachedProperties) {
NSString *propertyNewName = [self propertyKey:property.name];
if ([propertyNewName isEqualToString:oldName]) {
return property;
}
}
return nil;
}
这里cachedProperties
是目标模型对应的所有属性, 所以要进行一个转换,将目标模型的属性名对应的源模型的属性名找到,也就是propertyNewName
, 这里也是根据
+ (NSDictionary *)replacedKeyFromPropertyName
这个字典进行的, 因此这里可能是相对来说比较耗性能的, so, 就是这么简单, 其它的地方基本没什么变化, 就利用MJ的框架写出了自己的模型转模型方法, 以后调用的时候可以
BPDstModel *dstModel = [BPDstModel mj_object:srcModel];
这样一句话搞定, 是不是很简单?