ios項目依賴注入

依賴注入(Dependency Injection)

依賴注入最大的特點就是:幫助我們開發(fā)出松散耦合(loose coupled)、可維護、可測試的代碼和程序。這條原則的做法是大家熟知的面向接口琼牧,或者說是面向抽象編程。 眾所周知該編程思想在各大語言中都有體現(xiàn)如jave哀卫、C++巨坊、PHP以及.net中。當然設(shè)計模式的廣泛程度遠遠大于這些此改,iOS當然也不例外趾撵。 本文主要介紹本人在學習Dependency Injection的時候的學習過程以及對一些學習資料的總結(jié),主要介紹ios中的兩大框架ObjectionTyphoon带斑。 在Android上比較流行的有RoboGuiceDagger等.

什么是依賴注入(Dependency Injection)鼓寺?

依賴注入(Dependency Injection)是一個將行為從依賴中分離的技術(shù),簡單地說勋磕,它允許開發(fā)者定義一個方法函數(shù)依賴于外部其他各種交互妈候,而不需要編碼如何獲得這些外部交互的實例。 這樣就在各種組件之間解耦挂滓,從而獲得干凈的代碼苦银,相比依賴的硬編碼, 一個組件只有在運行時才調(diào)用其所需要的其他組件赶站,因此在代碼運行時幔虏,通過特定的框架或容器,將其所需要的其他依賴組件進行注入贝椿,主動推入想括。

依賴注入是最早spring和Piconcontainer等提出,如今已經(jīng)是一個缺省主流模式烙博,并擴展到前端如Angular.js等等瑟蜈。

1. 依賴

如果在Class A中烟逊,有Class B的實例,則稱Class A對Class B有一個依賴铺根。例如下面類ViewControllerA中用到一個ViewControllerB對象宪躯,我們就說類ViewControllerA對類ViewControllerB有一個依賴。

#import"ViewControllerB.h"@implementationViewControllerA- (void)buttonTapped{? ? ViewControllerB *vc = [[ViewControllerB alloc] init];? ? [self.navigationControllerpushViewController:vc animated:YES];}

仔細看這段代碼我們會發(fā)現(xiàn)存在一些問題:

(1). 如果現(xiàn)在要改變ViewControllerB生成方式位迂,如需要用initWithOrderid:(NSString * orderid)初始化vc访雪,需要修改ViewControllerA代碼;

(2). 如果想測試不同ViewControllerB對象對ViewControllerA的影響很困難掂林,因為ViewControllerB 的初始化被寫死在了ViewControllerA` 的構(gòu)造函數(shù)中臣缀;

(3). 如果[[ViewControllerB alloc] init]過程非常緩慢,單測時我們希望用已經(jīng)初始化好的ViewControllerB對象Mock掉這個過程也很困難党饮。

2. 依賴注入

上面將依賴在構(gòu)造函數(shù)中直接初始化是一種Hard init方式肝陪,弊端在于兩個類不夠獨立,不方便測試刑顺。我們還有另外一種Init方式氯窍,如下:

@interfaceViewControllerA()@property(nonatomic,readonly) ViewControllerB *vcB;@end@implementationViewControllerA// vcB是在ViewControllerA被創(chuàng)建之前被創(chuàng)建的并且作為參數(shù)傳進來,// 調(diào)用者如果想蹲堂,還可以自定義狼讨。- (instancetype)initWithEngine:(ViewControllerB *)vcB? {? ? ...? ? _vcB = vcB;returnself;? }@end

上面代碼中,我們將vcB對象作為構(gòu)造函數(shù)的一個參數(shù)傳入柒竞。在調(diào)用ViewControllerA的構(gòu)造方法之前外部就已經(jīng)初始化好了vcB對象政供。像這種非自己主動初始化依賴,而通過外部來傳入依賴的方式朽基,我們就稱為依賴注入布隔。

現(xiàn)在我們發(fā)現(xiàn)上面1中存在的兩個問題都很好解決了,簡單的說依賴注入主要有兩個好處:

解耦稼虎,將依賴之間解耦衅檀。

因為已經(jīng)解耦,所以方便做單元測試霎俩,尤其是Mock測試哀军。

那么問題來了,如何學習Dependency Injection呢 ?iOS有關(guān)DI依賴注入的框架比較好用的有兩個:Objection 和 Typhoon.下面就從幾個方便來介紹下這兩個框架

一:Objection 和 Typhoon這兩個框架有什么區(qū)別呢 其實這兩個框架各有優(yōu)勢:

Objection框架,使用起來比較靈活打却,用法比較簡單杉适。示例代碼如下:

屬性注冊:

@classEngine,Brakes;@interfaceCar:NSObject{? ? Engine *engine;? ? Brakes *brakes;BOOLawake;? }// Will be filled in by objection@property(nonatomic,strong) Engine *engine;// Will be filled in by objection@property(nonatomic,strong) Brakes *brakes;@property(nonatomic)BOOLawake;@implementationCarobjection_requires(@"engine", @"brakes")//屬性的依賴注入@synthesizeengine, brakes, awake;@end


方法注入:

@implementation Truckobjection_requires(@"engine", @"brakes")objection_initializer(truckWithMake:model:)//方法的依賴注入+ (instancetype)truckWithMake:(NSString *) make model: (NSString *)model {...}@end

2.對比來說Typhoon的使用起來就比較規(guī)范,首先需要創(chuàng)建一個TyphoonAssembly的子類柳击。其需要注入的方法和屬性都需要寫在這個統(tǒng)一個子類中猿推,當然可以實現(xiàn)不同的子類來完成不同的功能:

@interface MiddleAgesAssembly : TyphoonAssembly-(Knight*)basicKnight;-(Knight*)cavalryMan;-(id)defaultQuest;@end

屬性注入:

- (Knight*)cavalryMan{return[TyphoonDefinitionwithClass:[CavalryManclass]configuration:^(TyphoonDefinition*definition) {? ? [definitioninjectProperty:@selector(quest)with:[selfdefaultQuest]];? ? [definitioninjectProperty:@selector(damselsRescued)with:@(12)];}];}

方法注入:

- (Knight*)knightWithMethodInjection{return[TyphoonDefinitionwithClass:[Knightclass]configuration:^(TyphoonDefinition*definition) {? ? [definitioninjectMethod:@selector(setQuest:andDamselsRescued:)parameters:^(TyphoonMethod*method) {? ? ? ? [methodinjectParameterWith:[selfdefaultQuest]];? ? ? ? [methodinjectParameterWith:@321];? ? }];}];}

3.當然還有一些硬性的區(qū)別就是Typhoon現(xiàn)在已經(jīng)支持Swift

4.兩者維護時間都超過2年以上捌肴。

Tythoon官方介紹的優(yōu)勢:

1)Non-invasive. No macrosorXML required. Uses powerful ObjC runtime instrumentation.2)No magic strings – supports IDE refactoring, code-completionandcompile-timechecking.3)Provides full-modularizationandencapsulationofconfiguration details. Let your architecturetella story.4)Dependencies declaredinany order. (The orderthatmakes sensetohumans).5)Makesiteasytohave multiple configurationsofthesame base-classorprotocol.6)Supports injectionofview controllersandstoryboard integration. Supports both initializerandpropertyinjection, plus life-cycle management.7)Powerful memory management features. Provides pre-configured objects,withoutthememory overheadofsingletons.8)Excellent supportforcircular dependencies.9)Lean. Has a very low footprint, soisappropriateforCPUandmemory constrained devices.10)While being feature-packed, Typhoon weighs-inatjust3000linesofcodeintotal.11)Battle-tested — usedinall kindsofAppstore-featured apps.

大體翻譯過來:

1)非侵入性蹬叭。不需要宏或XML毯侦。使用強大的ObjC運行時儀器。2)沒有魔法字符串——支持IDE重構(gòu),完成和編譯時檢查具垫。3)提供full-modularization和封裝的配置細節(jié)。讓你的架構(gòu)告訴一個故事试幽。4)依賴關(guān)系中聲明的任何順序筝蚕。(對人類有意義的順序)。5)很容易有多個配置相同的基類或協(xié)議铺坞。6)支持注射的視圖控制器和故事板集成起宽。同時支持初始化器和屬性注入,以及生命周期管理。7)強大的內(nèi)存管理功能济榨。提供預(yù)配置對象,沒有單件的內(nèi)存開銷坯沪。8)優(yōu)秀的支持循環(huán)依賴。9)精益擒滑。占用很低,所以適合CPU和內(nèi)存受限的設(shè)備腐晾。10),功能強大,臺風重總共只有3000行代碼。11)久經(jīng)沙場,用于各種Appstore-featured應(yīng)用丐一。

針對這兩個框架網(wǎng)上教程并不多藻糖,收集了一些比較有用的資料。最主要的用法還得看官方文檔分別在:ObjectionTyphoon

objc.io官網(wǎng)的博文 Dependency Injection 和 Typhoon原創(chuàng)大神(Graham Lee)的文章Dependency Injection, iOS and You不看后悔一輩子^_^

Objection是一個輕量級的依賴注入框架库车,受Guice的啟發(fā)巨柒,Google Wallet 也是使用的該項目∧埽「依賴注入」是面向?qū)ο缶幊痰囊环N設(shè)計模式洋满,用來減少代碼之間的耦合度。通痴浞唬基于接口來實現(xiàn)牺勾,也就是說不需要new一個對象,而是通過相關(guān)的控制器來獲取對象垫蛆。2013年最火的php框架laravel就是其中的典型禽最。

假設(shè)有以下場景:ViewControllerA.view里有一個button,點擊之后push一個ViewControllerB袱饭,最簡單的寫法類似這樣:

-? (void)viewDidLoad{? ? [superviewDidLoad];UIButton*button = [UIButtonbuttonWithType:UIButtonTypeSystem];? ? button.frame= CGRectMake(100,100,100,30);? ? [button setTitle:@"Button"forState:UIControlStateNormal];? ? [button addTarget:selfaction:@selector(buttonTapped) forControlEvents:UIControlEventTouchUpInside];? ? [self.viewaddSubview:button];}- (void)buttonTapped{? ? ViewControllerB *vc = [[ViewControllerB alloc] init];? ? [self.navigationControllerpushViewController:vc animated:YES];}

這樣寫的一個問題是川无,ViewControllerA需要import ViewControllerB,也就是對ViewControllerB產(chǎn)生了依賴虑乖。依賴的東西越多懦趋,維護起來就越麻煩,也容易出現(xiàn)循環(huán)依賴的問題疹味,而objection正好可以處理這些問題仅叫。

實現(xiàn)方法是:先定義一個協(xié)議(protocol)帜篇,然后通過objection來注冊這個協(xié)議對應(yīng)的class,需要的時候诫咱,可以獲取該協(xié)議對應(yīng)的object笙隙。對于使用方無需關(guān)心到底使用的是哪個Class,反正該有的方法坎缭、屬性都有了(在協(xié)議中指定)竟痰。這樣就去除了對某個特定Class的依賴。也就是通常所說的「面向接口編程」掏呼。

JSObjectionInjector *injector = [JSObjection defaultInjector];// [1]UIViewController *vc = [injector getObject:@protocol(ViewControllerAProtocol)]; // [2]vc.backgroundColor= [UIColorlightGrayColor];// [3]UINavigationController*nc = [[UINavigationControlleralloc] initWithRootViewController:vc];self.window.rootViewController= nc;

[1] 獲取默認的injector坏快,這個injector已經(jīng)注冊過ViewControllerAProtocol了。

[2] 獲取ViewControllerAProtocol對應(yīng)的Object憎夷。

[3] 拿到VC后莽鸿,設(shè)置它的某些屬性,比如這里的backgroundColor拾给,因為在ViewControllerAProtocol里有定義這個屬性祥得,所以不會有warning。

可以看到這里沒有引用ViewControllerA蒋得。再來看看這個ViewControllerAProtocol是如何注冊到injector中的啃沪,這里涉及到了Module,對Protocol的注冊都是在Module中完成的窄锅。Module只要繼承JSObjectionModule這個Class即可创千。

@interface ViewControllerAModule : JSObjectionModule@end@implementation ViewControllerAModule+ (void)load{? ? JSObjectionInjector *injector = [JSObjection defaultInjector];? ? injector = injector ? : [JSObjection createInjector];? ? injector = [injector withModule:[[ViewControllerAModule alloc] init]];? ? [JSObjection setDefaultInjector:injector]; }- (void)configure{? ? [self bindClass:[ViewControllerA class] toProtocol:@protocol(ViewControllerAProtocol)];}@end

綁定操作是在configure方法里進行的,這個方法在被添加到injector里時會被自動觸發(fā)入偷。

JSObjectionInjector *injector = [JSObjection defaultInjector]; // [1]injector = injector ? : [JSObjection createInjector]; // [2]injector = [injector withModule:[[ViewControllerAModule alloc] init]]; // [3][JSObjection setDefaultInjector:injector]; // [4]

[1] 獲取默認的injector

[2] 如果默認的injector不存在追驴,就新建一個

[3] 往這個injector里注冊我們的Module

[4] 設(shè)置該injector為默認的injector

這段代碼可以直接放到+ (void)load里執(zhí)行,這樣就可以避免在AppDelegate里import各種Module疏之。

因為我們無法直接獲得對應(yīng)的Class殿雪,所以必須要在協(xié)議里定義好對外暴露的方法和屬性,然后該Class也要實現(xiàn)該協(xié)議锋爪。

@protocolViewControllerAProtocol@property(nonatomic) NSUInteger currentIndex;@property(nonatomic)UIColor*backgroundColor;@end@interfaceViewControllerA:UIViewController@end

通過Objection實現(xiàn)依賴注入后丙曙,就能更好地實現(xiàn)SRP(Single Responsibility Principle),代碼更簡潔其骄,心情更舒暢亏镰,生活更美好。

總體來說拯爽,這個lib還是挺靠譜的索抓,已經(jīng)維護了兩年多,也有一些項目在用,對于提高開發(fā)成員的效率也會有不少的幫助逼肯,可以考慮嘗試下耸黑。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市篮幢,隨后出現(xiàn)的幾起案子大刊,更是在濱河造成了極大的恐慌,老刑警劉巖三椿,帶你破解...
    沈念sama閱讀 221,331評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奈揍,死亡現(xiàn)場離奇詭異,居然都是意外死亡赋续,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評論 3 398
  • 文/潘曉璐 我一進店門另患,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纽乱,“玉大人,你說我怎么就攤上這事昆箕⊙涣校” “怎么了?”我有些...
    開封第一講書人閱讀 167,755評論 0 360
  • 文/不壞的土叔 我叫張陵鹏倘,是天一觀的道長薯嗤。 經(jīng)常有香客問我,道長纤泵,這世上最難降的妖魔是什么骆姐? 我笑而不...
    開封第一講書人閱讀 59,528評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮捏题,結(jié)果婚禮上玻褪,老公的妹妹穿的比我還像新娘。我一直安慰自己公荧,他們只是感情好带射,可當我...
    茶點故事閱讀 68,526評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著循狰,像睡著了一般窟社。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绪钥,一...
    開封第一講書人閱讀 52,166評論 1 308
  • 那天灿里,我揣著相機與錄音,去河邊找鬼程腹。 笑死钠四,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缀去,決...
    沈念sama閱讀 40,768評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼侣灶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了缕碎?” 一聲冷哼從身側(cè)響起褥影,我...
    開封第一講書人閱讀 39,664評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咏雌,沒想到半個月后凡怎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,205評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡赊抖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,290評論 3 340
  • 正文 我和宋清朗相戀三年统倒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氛雪。...
    茶點故事閱讀 40,435評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡房匆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出报亩,到底是詐尸還是另有隱情浴鸿,我是刑警寧澤,帶...
    沈念sama閱讀 36,126評論 5 349
  • 正文 年R本政府宣布弦追,位于F島的核電站岳链,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏劲件。R本人自食惡果不足惜掸哑,卻給世界環(huán)境...
    茶點故事閱讀 41,804評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望零远。 院中可真熱鬧举户,春花似錦、人聲如沸遍烦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽服猪。三九已至供填,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罢猪,已是汗流浹背近她。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留膳帕,地道東北人粘捎。 一個月前我還...
    沈念sama閱讀 48,818評論 3 376
  • 正文 我出身青樓薇缅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親攒磨。 傳聞我的和親對象是個殘疾皇子泳桦,可洞房花燭夜當晚...
    茶點故事閱讀 45,442評論 2 359

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