UIViewController解耦嘗試

當咱們使用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"
}

其中Test1Test2被稱爲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主頁

相關文章
相關標籤/搜索