原創(chuàng)文章轉(zhuǎn)載請注明出處尽纽,謝謝
最近在項目中遇到一個問題往湿,我們的業(yè)務邏輯中存在4種Purpose Type害碾,每種Purpose Type又有5種Status type俘闯,所以就有20種組合,每種組合出來的type都會對應一個string肃拜,而且未來type的數(shù)量可能會越來越多痴腌,所以這個如果用switch,if等來操作判斷對應的字符串顯然是不合理的燃领,太冗余不利于擴展士聪,采用數(shù)據(jù)字典的方式也不太好還需要自己手動建,每次更新也要手動修改猛蔽,最好的方式是我們可以直接通過兩個type就組合出對應的string剥悟,但是OC中反射只是針對于Class,對于基礎類型的操作我們只能依賴于C++來幫我們完成曼库。
如下有這兩個enum区岗,可以組合成20種type的string
typedef NS_ENUM(NSUInteger, BMTAccountType) {
kBMTAccountTypeUnknow = 0,
kBMTAccountTypeConception = 1,
kBMTAccountTypeContraception = 2,
kBMTAccountTypeMenstruation = 4,
kBMTAccountTypePregnancyMonitor = 8,
};
typedef NS_ENUM(NSInteger, BMTMenstruationPeriodStatusType) {
kReportCalendarDayTypeUnknown = -1,
kReportCalendarDayTypeNormal,
kReportCalendarDayTypeMenustration,
kReportCalendarDayTypeOvulation,
kReportCalendarDayTypeCalculateMenstruation,
kReportCalendarDayTypeOvulationDays
};
我們的目標是可以自動組合成以下的方式直接調(diào)用(命名不是很優(yōu)雅)。
Hint.kBMTAccountTypeConception.kReportCalendarDayTypeNormal.Title = "XXX";
Hint.kBMTAccountTypeConception.kReportCalendarDayTypeOvulation.Title = "XXX";
Hint.kBMTAccountTypeConception.kReportCalendarDayTypeMenustration.Title = "XXX";
Hint.kBMTAccountTypeConception.kReportCalendarDayTypeOvulationDays.Title = "XXX";
Hint.kBMTAccountTypeContraception.kReportCalendarDayTypeNormal.Title = "XXX";
Hint.kBMTAccountTypeContraception.kReportCalendarDayTypeOvulation.Title = "XXX";
Hint.kBMTAccountTypeContraception.kReportCalendarDayTypeMenustration.Title = "XXX";
Hint.kBMTAccountTypeContraception.kReportCalendarDayTypeOvulationDays.Title = "XXX";
Hint.kBMTAccountTypeMenstruation.kReportCalendarDayTypeNormal.Title = "XXX";
Hint.kBMTAccountTypeMenstruation.kReportCalendarDayTypeOvulation.Title = "XXX";
Hint.kBMTAccountTypeMenstruation.kReportCalendarDayTypeMenustration.Title = "XXX";
Hint.kBMTAccountTypeMenstruation.kReportCalendarDayTypeOvulationDays.Title = "XXX";
Hint.kBMTAccountTypePregnancyMonitor.kReportCalendarDayTypeNormal.Title = "XXX";
Hint.kBMTAccountTypePregnancyMonitor.kReportCalendarDayTypeOvulation.Title = "XXX";
Hint.kBMTAccountTypePregnancyMonitor.kReportCalendarDayTypeMenustration.Title = "XXX";
Hint.kBMTAccountTypePregnancyMonitor.kReportCalendarDayTypeOvulationDays.Title = "XXX";
我們的基本思路是自定義NS_ENUM毁枯,然后使用__VA_ARGS__進行字符串解析慈缔,去除空格和等號,最后反射出結(jié)果,如下:
#ifndef BMT_REFLECT_ENUM_H
#define BMT_REFLECT_ENUM_H
NSString *reflectString(NSString *enumDefinition, NSNumber *number);
__attribute__((overloadable)) NSString *BMTReflectStringForMember(NSInteger value);
#define BMT_REFLECTABLE_ENUM(type, name, ...) \
typedef NS_ENUM(type, name) { \
__VA_ARGS__ \
}; \
\
__attribute__((overloadable)) static inline NSString *BMTReflectStringForMember(name value) { \
return reflectString(@(#__VA_ARGS__), @(value)); \
} \
#endif
- (NSArray *)makeArray:(id(^)(id obj))block {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:[self count]];
for(id obj in self) {
[array addObject:block(obj)];
}
return array;
}
- (void)setupWithEnumDefinition:(NSString *)enumDefinition {
NSNumberFormatter *numberFormatter = [NSNumberFormatter new];
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
NSInteger currentIndex = -1;
NSMutableDictionary *mutableDictionary = [NSMutableDictionary new];
for (NSString *member in [enumDefinition componentsSeparatedByString:@","]) {
NSArray *parts = [[member componentsSeparatedByString:@"="] makeArray:^NSString *(NSString *element) {
return [element stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}];
NSAssert(parts.count == 1 || parts.count == 2, @"Reflect Enum illegal member definition");
NSString *name = [parts firstObject];
if (parts.count == 2) {
NSString *valueString = [parts lastObject];
NSNumber *number = [numberFormatter numberFromString:valueString];
if (!number) {
NSAssert(NO, @"Reflect Enum get string for the enum with duplicated values");
} else {
NSString *matchingObject = [mutableDictionary objectForKey:number];
if (matchingObject) {
NSAssert(NO, @"Reflect Enum get string for the enum with duplicated values");
}
}
currentIndex = [number longLongValue];
} else {
currentIndex++;
}
mutableDictionary[@(currentIndex)] = name;
}
_mapFromValueToString = [mutableDictionary copy];
}
這里有幾個需要注意點:
* 我們需要緩存每個枚舉解析后的值种玛,防止浪費資源
* 我們的反射枚舉類型中不能包括相同的枚舉值藐鹤,key-value是唯一對應的
// Demo one
REFLECTABLE_ENUM(NSInteger,
ThirdEnum,
ThirdEnum1 = 0,
ThirdEnum2 = ThirdEnum1,
ThirdEnum3,
ThirdEnum4
);
// Demo two
REFLECTABLE_ENUM(NSInteger,
ThirdEnum,
ThirdEnum1 = 0,
ThirdEnum2 = 0,
ThirdEnum3,
ThirdEnum4
);
github上有一個項目就是做了這件事瓤檐,不過由于這個功能比較簡單,所以我們沒有必要去依賴這個庫教藻,自己寫反而比較方便距帅,而且這個項目有一個小bug。就是上面的例子括堤,為了保持key-value的唯一對應碌秸,我們需要處理上面的兩種情況,但是項目里僅處理了第一種情況悄窃,但第二種情況其實也是需要處理的讥电。
我提交的代碼通過審核BugFix了。
4.pic.jpg