當咱們使用UIViewController時,從一個ViewController跳到另一個ViewController,最簡單的代碼(不用storyboard的狀況下)就是alloc一個實例,而後用navigation controller去push它。好比git
#import "ViewController2.h" //..... - (void)buttonPushOnTouch:(id)sender { ViewController2 *vc2 = [[ViewController2 alloc]init]]; [self.navigationViewController pushViewController:vc2 animate:YES]; }
而這樣一來,這個ViewController必須知道另一個ViewController的存在,並且須要import它的頭文件。這樣一來,兩個ViewController就緊密耦合了。github
在通常的程序裏面,這麼作沒有任何問題。可是咱們公司的兩個主打產品有一個特殊需求就是:兩個產品共用部分界面,而這些共用界面中的一些按鈕,點擊以後,在不一樣的程序裏須要push出不一樣的界面(界面初始化須要的參數是同樣的,但就是徹底不一樣的界面)。而這兩個產品又是由不一樣的小組去完成的,他們不共享同一個工程。json
這時,咱們想到的是將這些共用界面提取出來作成framework。那麼問題就出現了:我若是在framework裏面的view controller新建一個下一級view controller,就須要知道下一級view controller的頭文件,下一級view controller又實際存在於每個主工程裏面。這樣就出現了循環依賴的狀況,這樣作出來的framework即便能編譯經過也無法維護。app
好在ObjC是一個動態語言,實際上咱們根本不須要知道一個類的信息就能初始化它。使用的方法就是NSClassFromString()
函數,此時你只要知道class名字就行,若是它返回一個Class
不爲nil
,就能夠實例化它,而且用KVC注入須要的property。因此稍加改進的代碼以下:函數
NSString *viewControllerClassName = IN_PROJ_1?@"AAA":@"BBB"; Class aViewControllerClass = NSClassFromString(viewControllerClassName); if(aViewControllerClass != nil){ id anInstance = [[aViewControllerClass alloc]init]; [anInstance setValue:xxx forKeyPath:@"xxx"]; if (anInstance != nil){ [self.navigationController pushViewController:anInstance animate:YES]; } }
雖然這個能解耦,可是每次寫那麼冗餘的代碼總歸不易維護。因此我嘗試把這些東西抽象出來,以配置文件的形式,在不一樣工程裏配置不一樣的界面。靈感來自於前幾年在寫Java的時候用的Spring。最後作了一個叫作EMViewControllerManager的東西。ui
EMControllerManager的配置文件的基本結構以下:code
{ "Test1":{ "ClassName":"Test1ViewController", "Description":"The homepage of the app", "Tag":"100", "Dependencies":{ "dependentString":"@Yes, you can inject a string using the config file", "dependentInt":1000, "dependentBool":true, "test2ViewController":"Test2" } }, "Test2":"Test2ViewController" }
其中Test1
、Test2
被稱爲view controller名稱(暱稱)。ClassName
指定了view controller的真實類名,而Dependencies
能夠作一點基礎的DI功能。對象
而後在AppDelegate
裏面載入配置文件:ip
EMControllerManager *cm = [EMControllerManager sharedInstance]; NSString *path = [[NSBundle mainBundle]pathForResource:@"ViewControllerConfig" ofType:@"json"]; NSError *e = nil; [cm loadConfigFileOfPath:path fileType:EMControllerManagerConfigFileTypeJSON error:&e]; if (e) { NSLog(@"%@",[e localizedDescription]); }
而在view controller裏面,能夠這麼來初始化一個view controller對象:ci
UIViewController *vc = [cm createViewControllerInstanceNamed:@"Test1" withPropertyValues:@{@"color":[UIColor redColor],@"number":@(1)}]; if(vc != nil){ [self.navigationController pushViewController:vc animate:YES] }
其中Named:
是view controller的暱稱,withPropertyValues:
是初始化以後立刻注入的property。
不一樣的工程只要改配置文件,而不須要改具體的代碼,就能達到差別化的效果。
具體用法請閱讀EMControllerManager的Github主頁