当咱们使用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主页