使用objection來(lái)模塊化開(kāi)發(fā)iOS項(xiàng)目
objection 是一個(gè)輕量級(jí)的依賴注入框架,受Guice的啟發(fā),Google Wallet 也是使用的該項(xiàng)目。「依賴注入」是面向?qū)ο缶幊痰囊环N設(shè)計(jì)模式,用來(lái)減少代碼之間的耦合度。通?;诮涌趤?lái)實(shí)現(xiàn),也就是說(shuō)不需要new一個(gè)對(duì)象,而是通過(guò) 相關(guān)的控制器來(lái)獲取對(duì)象。2013年最火的PHP框架 laravel 就是其中的典型。
假設(shè)有以下場(chǎng)景:ViewControllerA.view里有一個(gè)button,點(diǎn)擊之后push一個(gè)ViewControllerB,最簡(jiǎn)單的寫(xiě)法類似這樣:
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
- button.frame = CGRectMake(100, 100, 100, 30);
- [button setTitle:@"Button" forState:UIControlStateNormal];
- [button addTarget:self action:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];
- [self.view addSubview:button];
- }
- - (void)buttonTapped
- {
- ViewControllerB *vc = [[ViewControllerB alloc] init];
- [self.navigationController pushViewController:vc animated:YES];
- }
這樣寫(xiě)的一個(gè)問(wèn)題是,ViewControllerA需要import ViewControllerB,也就是對(duì)ViewControllerB產(chǎn)生了依賴。依賴的東西越多,維護(hù)起來(lái)就越麻煩,也容易出現(xiàn)循環(huán)依賴的問(wèn)題,而objection正好可以處理這些問(wèn)題。
實(shí)現(xiàn)方法是:先定義一個(gè)協(xié)議(protocol),然后通過(guò)objection來(lái)注冊(cè)這個(gè)協(xié)議對(duì)應(yīng)的class,需要的時(shí)候,可以獲取該協(xié)議對(duì) 應(yīng)的object。對(duì)于使用方無(wú)需關(guān)心到底使用的是哪個(gè)Class,反正該有的方法、屬性都有了(在協(xié)議中指定)。這樣就去除了對(duì)某個(gè)特定Class的依 賴。也就是通常所說(shuō)的「面向接口編程」。
- JSObjectionInjector *injector = [JSObjection defaultInjector]; // [1]
- UIViewController <ViewControllerAProtocol> *vc = [injector getObject:@protocol(ViewControllerAProtocol)]; // [2]
- vc.backgroundColor = [UIColor lightGrayColor]; // [3]
- UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
- self.window.rootViewController = nc;
[1] 獲取默認(rèn)的injector,這個(gè)injector已經(jīng)注冊(cè)過(guò)ViewControllerAProtocol了。
[2] 獲取ViewControllerAProtocol對(duì)應(yīng)的Object。
[3] 拿到VC后,設(shè)置它的某些屬性,比如這里的backgroundColor,因?yàn)樵赩iewControllerAProtocol里有定義這個(gè)屬性,所以不會(huì)有warning。
可以看到這里沒(méi)有引用ViewControllerA。再來(lái)看看這個(gè)ViewControllerAProtocol是如何注冊(cè)到 injector中的,這里涉及到了Module,對(duì)Protocol的注冊(cè)都是在Module中完成的。Module只要繼承 JSObjectionModule這個(gè)Class即可。
- @interface ViewControllerAModule : JSObjectionModule
- @end
- @implementation ViewControllerAModule
- - (void)configure
- {
- [self bindClass:[ViewControllerA class] toProtocol:@protocol(ViewControllerAProtocol)];
- }
- @end
綁定操作是在configure方法里進(jìn)行的,這個(gè)方法在被添加到injector里時(shí)會(huì)被自動(dòng)觸發(fā)。
- JSObjectionInjector *injector = [JSObjection defaultInjector]; // [1]
- injector = injector ? : [JSObjection createInjector]; // [2]
- injector = [injector withModule:[[ViewControllerAModule alloc] init]]; // [3]
- [JSObjection setDefaultInjector:injector]; // [4]
[1] 獲取默認(rèn)的 injector
[2] 如果默認(rèn)的 injector 不存在,就新建一個(gè)
[3] 往這個(gè) injector 里注冊(cè)我們的 Module
[4] 設(shè)置該 injector 為默認(rèn)的 injector
這段代碼可以直接放到 + (void)load里執(zhí)行,這樣就可以避免在AppDelegate里import各種Module。
因?yàn)槲覀儫o(wú)法直接獲得對(duì)應(yīng)的Class,所以必須要在協(xié)議里定義好對(duì)外暴露的方法和屬性,然后該Class也要實(shí)現(xiàn)該協(xié)議。
- @protocol ViewControllerAProtocol <NSObject>
- @property (nonatomic) NSUInteger currentIndex;
- @property (nonatomic) UIColor *backgroundColor;
- @end
- @interface ViewControllerA : UIViewController <ViewControllerAProtocol>
- @end
通過(guò)objection實(shí)現(xiàn)依賴注入后,就能更好地實(shí)現(xiàn)SRP(Single Responsibility Principle),代碼更簡(jiǎn)潔,心情更舒暢,生活更美好。拿Pinterest來(lái)說(shuō),下面的頁(yè)面就可以劃分為3個(gè)Section。
各個(gè)Section可以由不同的人負(fù)責(zé),然后串到一起就行,也能一定程度地避免MVC(Mess View Controller)的出現(xiàn)。
總體來(lái)說(shuō),這個(gè)lib還是挺靠譜的,已經(jīng)維護(hù)了兩年多,也有一些項(xiàng)目在用,對(duì)于提高開(kāi)發(fā)成員的效率也會(huì)有不少的幫助,可以考慮嘗試下。
轉(zhuǎn)自無(wú)網(wǎng)不剩的博客