Skip to content

Commit

Permalink
Avoid some redundant code through bunches of benchmarks.
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfcon committed Dec 27, 2021
1 parent 27e1d26 commit 69a5a90
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@
shouldUseLaunchSchemeArgsEnv = "YES">
<TestPlans>
<TestPlanReference
reference = "container:MJExtensionTests/TestAllConversionCorrectness.xctestplan">
reference = "container:MJExtensionTests/Plans/TestAllConversionCorrectness.xctestplan">
</TestPlanReference>
<TestPlanReference
reference = "container:MJExtensionTests/MJExtension.xctestplan"
reference = "container:MJExtensionTests/Plans/MJExtension.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
Expand Down
2 changes: 0 additions & 2 deletions MJExtension/MJEClass.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ NS_ASSUME_NONNULL_BEGIN
NSArray<MJProperty *> * _Nullable _allCodingProperties;

NSArray<MJProperty *> * _Nullable _allProperties2JSON;
NSDictionary <NSString *, MJProperty *> *_mapper;
NSArray<MJProperty *> *_multiKeysProperties;

BOOL _hasOld2NewModifier;
BOOL _hasDictionary2ObjectModifier;
Expand Down
11 changes: 0 additions & 11 deletions MJExtension/MJEClass.m
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,6 @@ - (void)mj_handlePropertiesWithAllowedList:(NSSet *)allowedList
inClass:(Class)cls {
NSMutableArray<MJProperty *> *allProperties = [NSMutableArray array];
NSMutableArray<MJProperty *> *codingProperties = [NSMutableArray array];
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray<MJProperty *> *multiKeysProperties = [NSMutableArray array];
// TODO: 4.0.0 new feature
// NSMutableArray<MJProperty *> *allProperties2JSON = [NSMutableArray array];
[cls mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) {
Expand Down Expand Up @@ -204,13 +202,6 @@ - (void)mj_handlePropertiesWithAllowedList:(NSSet *)allowedList

// handle keypath / keypath array / keypath array(with subkey)
[property handleOriginKey:key];
// The property matched with a singular key is the only condition for dictionary enumeration.
if (property->_isMultiMapping) {
[multiKeysProperties addObject:property];
} else {
property->_nextSame = mapper[property->_mappedKey] ?: nil;
mapper[property->_mappedKey] = property;
}

// handle generic class
id clazz = genericClasses[property.name];
Expand All @@ -228,8 +219,6 @@ - (void)mj_handlePropertiesWithAllowedList:(NSSet *)allowedList

_allProperties = allProperties.copy;
_allCodingProperties = codingProperties.copy;
_mapper = mapper.copy;
_multiKeysProperties = multiKeysProperties.copy;

_propertiesCount = _allProperties.count;
}
Expand Down
2 changes: 0 additions & 2 deletions MJExtension/MJProperty.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ NS_ASSUME_NONNULL_BEGIN
MJEBasicType _basicObjectType;
/// True if property is a number (e.g: bool, double, int, etc.).
BOOL _isBasicNumber;
/// The property has the same value with it. It's a linked list data structure for different property linked by the same key, which will result to get the same value.
MJProperty * _Nullable _nextSame;
}

/// Property name that defined by class.
Expand Down
256 changes: 118 additions & 138 deletions MJExtension/NSObject+MJKeyValue.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,31 +93,14 @@ - (instancetype)mj_setKeyValues:(id)keyValues

MJExtensionAssertError([object isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典");

MJEClass *mjeClass = [MJEClass cachedClass:self.class];
MJEClass *classCache = [MJEClass cachedClass:self.class];
NSDictionary *dict = object;
// 在循环数量超出不多的情况下, 优先按所有属性列表遍历, threshold = 1.3
if (mjeClass->_propertiesCount < dict.count * 1.3) {
[self mj_enumerateProperties:mjeClass->_allProperties
withDictionary:dict classCache:mjeClass
context:context];
} else {
for (NSString *key in dict) {
id value = dict[key];
MJProperty *property = mjeClass->_mapper[key];
while (property) {
[self mj_setValue:value forProperty:property
context:context classCache:mjeClass];
property = property->_nextSame;
}
}
if (mjeClass->_multiKeysProperties.count) {
[self mj_enumerateProperties:mjeClass->_multiKeysProperties
withDictionary:dict classCache:mjeClass context:context];
}
}

[self mj_enumerateProperties:classCache->_allProperties
withDictionary:dict classCache:classCache context:context];

// 转换完毕
if (mjeClass->_hasDictionary2ObjectModifier) {
if (classCache->_hasDictionary2ObjectModifier) {
[self mj_didConvertToObjectWithKeyValues:keyValues];
}
return self;
Expand All @@ -143,130 +126,127 @@ - (void)mj_enumerateProperties:(NSArray<MJProperty *> *)properties
}
}

[self mj_setValue:value forProperty:property
context:context classCache:classCache];

} @catch (NSException *exception) {
MJExtensionBuildError([self class], exception.reason);
MJExtensionLog(@"%@", exception);
#ifdef DEBUG
[exception raise];
#endif
}
}
}

- (void)mj_setValue:(id)value forProperty:(MJProperty *)property
context:(NSManagedObjectContext *)context
classCache:(MJEClass *)classCache {
if (classCache->_hasOld2NewModifier
&& property->_hasValueModifier) {
id newValue = [self mj_newValueFromOldValue:value property:property];
if (newValue != value) { // 有过滤后的新值
[property setValue:newValue forObject:self];
return;
}
}

// 如果没有值,就直接返回
if (!value || value == NSNull.null) return;
// 2.复杂处理
MJEPropertyType type = property.type;
Class propertyClass = property.typeClass;
Class objectClass = property.classInCollection;

// 不可变 -> 可变处理
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 (property->_basicObjectType == MJEBasicTypeUndefined && propertyClass) { // 模型属性
value = [propertyClass mj_objectWithKeyValues:value context:context];
} 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];
if (classCache->_hasOld2NewModifier
&& property->_hasValueModifier) {
id newValue = [self mj_newValueFromOldValue:value property:property];
if (newValue != value) { // 有过滤后的新值
[property setValue:newValue forObject:self];
return;
}
}
value = urlArray;
} else { // 字典数组-->模型数组
value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context];
}
} 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 == MJEPropertyTypeLongDouble) {
long double num = [value mj_longDoubleValueWithLocale:classCache->_locale];
mj_selfSend(property.setter, long double, num);
return;
} else if (property->_basicObjectType == MJEBasicTypeData || property->_basicObjectType == MJEBasicTypeMutableData) {
value = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding].mutableCopy;
} else if (property.isNumber) {
NSString *oldValue = value;

// NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出
NSDecimalNumber *decimalValue = [NSDecimalNumber
decimalNumberWithString:oldValue
locale:classCache->_locale];
// 如果没有值,就直接返回
if (!value || value == NSNull.null) return;
// 2.复杂处理
MJEPropertyType type = property.type;
Class propertyClass = property.typeClass;
Class objectClass = property.classInCollection;

// 检查特殊情况
if (decimalValue == NSDecimalNumber.notANumber) {
value = @(0);
} else if (propertyClass != [NSDecimalNumber class]) {
value = [decimalValue mj_standardValueWithType:type];
} else {
value = decimalValue;
// 不可变 -> 可变处理
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];
}

// 如果是BOOL
if (type == MJEPropertyTypeBool || type == MJEPropertyTypeInt8) {
// 字符串转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;
if (property->_basicObjectType == MJEBasicTypeUndefined && propertyClass) { // 模型属性
value = [propertyClass mj_objectWithKeyValues:value context:context];
} 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:context];
}
} 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 == MJEPropertyTypeLongDouble) {
long double num = [value mj_longDoubleValueWithLocale:classCache->_locale];
mj_selfSend(property.setter, long double, num);
return;
} else if (property->_basicObjectType == MJEBasicTypeData || property->_basicObjectType == MJEBasicTypeMutableData) {
value = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding].mutableCopy;
} else if (property.isNumber) {
NSString *oldValue = value;

// NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出
NSDecimalNumber *decimalValue = [NSDecimalNumber
decimalNumberWithString:oldValue
locale:classCache->_locale];

// 检查特殊情况
if (decimalValue == NSDecimalNumber.notANumber) {
value = @(0);
} else if (propertyClass != [NSDecimalNumber class]) {
value = [decimalValue mj_standardValueWithType:type];
} else {
value = decimalValue;
}

// 如果是BOOL
if (type == MJEPropertyTypeBool || type == MJEPropertyTypeInt8) {
// 字符串转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;
}
}
}
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
// 过滤 NSDecimalNumber类型
if (![value isKindOfClass:[NSDecimalNumber class]]) {
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
}
}

// 经过转换后, 最终检查 value 与 property 是否匹配
if (propertyClass && ![value isKindOfClass:propertyClass]) {
value = nil;
}

// 3.赋值
// long double 是不支持 KVC 的
if (property.type == MJEPropertyTypeLongDouble) {
mj_selfSend(property.setter, long double, ((NSNumber *)value).doubleValue);
return;
} else {
//FIXME: Bottleneck #4: Do not call method
[property setValue:value forObject:self];
// if (!property->_isKVCCompliant || value == nil) return;
// //FIXME: Bottleneck #4: Enhanced
// [self setValue:value forKey:property.name];
// // mj_msgSendOne(object, _setter, id, value);
}
} @catch (NSException *exception) {
MJExtensionBuildError([self class], exception.reason);
MJExtensionLog(@"%@", exception);
#ifdef DEBUG
[exception raise];
#endif
}
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
// 过滤 NSDecimalNumber类型
if (![value isKindOfClass:[NSDecimalNumber class]]) {
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
}
}

// 经过转换后, 最终检查 value 与 property 是否匹配
if (propertyClass && ![value isKindOfClass:propertyClass]) {
value = nil;
}

// 3.赋值
// long double 是不支持 KVC 的
if (property.type == MJEPropertyTypeLongDouble) {
mj_selfSend(property.setter, long double, ((NSNumber *)value).doubleValue);
return;
} else {
[property setValue:value forObject:self];
}
}

Expand Down

0 comments on commit 69a5a90

Please sign in to comment.