實現(xiàn)NS_ENUM的自定義反射

原創(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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轧抗,一起剝皮案震驚了整個濱河市恩敌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌横媚,老刑警劉巖纠炮,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灯蝴,居然都是意外死亡恢口,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進店門穷躁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕肩,“玉大人,你說我怎么就攤上這事问潭≡持睿” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵狡忙,是天一觀的道長梳虽。 經(jīng)常有香客問我,道長去枷,這世上最難降的妖魔是什么怖辆? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮删顶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘淑廊。我一直安慰自己逗余,他們只是感情好,可當我...
    茶點故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布季惩。 她就那樣靜靜地躺著录粱,像睡著了一般腻格。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啥繁,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天菜职,我揣著相機與錄音,去河邊找鬼旗闽。 笑死酬核,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的适室。 我是一名探鬼主播嫡意,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼捣辆!你這毒婦竟也來了蔬螟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤汽畴,失蹤者是張志新(化名)和其女友劉穎旧巾,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忍些,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡鲁猩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坐昙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绳匀。...
    茶點故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炸客,靈堂內(nèi)的尸體忽然破棺而出疾棵,到底是詐尸還是另有隱情,我是刑警寧澤痹仙,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布是尔,位于F島的核電站,受9級特大地震影響开仰,放射性物質(zhì)發(fā)生泄漏拟枚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一众弓、第九天 我趴在偏房一處隱蔽的房頂上張望恩溅。 院中可真熱鬧,春花似錦谓娃、人聲如沸脚乡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奶稠。三九已至俯艰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锌订,已是汗流浹背竹握。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辆飘,地道東北人啦辐。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像劈猪,于是被迫代替她去往敵國和親昧甘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容