iOS 依賴注入與Objection

Created by Linxi 2020/05/01

依賴注入

首先先說(shuō)明什么叫做依賴注入

比如AController跳轉(zhuǎn)到BController,那么這時(shí)候BController就需要在AController內(nèi)部進(jìn)行實(shí)例化箕别,如下

@implementation AController : UIViewController

...

- (void)jump 
{
    BController *bController = [[BController alloc] init];
    [self.navigationController pushViewController:bController animated:YES];
}

@end

這么做的話,當(dāng)AController被封裝成組件之后剧防,BController的配置將會(huì)被限制端幼,外部無(wú)法改變BController任何細(xì)節(jié),所以我們 ** 稍 加 改 進(jìn) **

@implementation AController : UIViewController

...

- (instancetype)initWithCreateBlock:(UIViewController *(^)(void))createBViewControllerBlock {
    ....
    self.createBViewControllerBlock = createBViewControllerBlock;
    ...
}

- (void)jump 
{
    UIViewController *bController = self.createBViewControllerBlock();
    [self.navigationController pushViewController:bController animated:YES];
}

@end

[[AController alloc] initWithCreateBlock:UIViewController* ^{
    BController *bController = [[BController alloc] initWithTitle:@"xxx"];
    return bController;
}];

將BController的創(chuàng)建通過(guò)Block暴露出來(lái)簇宽,AController內(nèi)部不關(guān)心BController是如何被創(chuàng)建的勋篓,那么AController對(duì)BController的依賴將通過(guò)外部的Block進(jìn)行注入。

這魏割,就是依賴注入譬嚣。

當(dāng)然這是最簡(jiǎn)單的依賴注入,無(wú)法滿足我們復(fù)雜的需求钞它,所以有時(shí)候我們需要使用第三方框架拜银,如ObjectionTyphoon

Objection

接下來(lái)說(shuō)明一下Objection的使用

Objection 是一個(gè)依賴注入框架,能夠在你獲取一個(gè)類的實(shí)例的時(shí)候遭垛,這個(gè)類內(nèi)部的屬性也同時(shí)會(huì)被實(shí)例化盐股。舉個(gè)例子:

//Car.h

@class Engine,Break;

@interface Car : NSObject

@property (nonatomic, strong) Engine *engine;

@property (nonatomic, strong) Break *breaks;

@end

//Car.m

#import <Objection/Objection.h>

@implementation Car
objection_requires(@"engine", @"breaks")

@end

創(chuàng)建一個(gè)默認(rèn)注射器

JSObjectionInjector *injector = [JSObjection createInjector];
[JSObjection setDefaultInjector:injector];

實(shí)例化Car對(duì)象

Car *car = [[JSObjection defaultInjector] getObject:[Car class]];

這時(shí)候所依賴的engine對(duì)象和breaks對(duì)象都會(huì)通過(guò)init方法實(shí)例化

最后打印屬性

car <Car: 0x6000006d8480> engine <Engine: 0x6000004841b0> breaks <Break: 0x6000004841e0>

假如說(shuō)Car對(duì)象不能通過(guò)init或者initWithXXX等自定義構(gòu)造方法去實(shí)例化,那么我們需要指定方法耻卡,讓注射器在指定的方法構(gòu)建依賴

@implementation Car
objection_requires(@"engine", @"breaks")
- (void)awakeFromNib {
  [[JSObjection defaultInjector] injectDependencies:self];
}
@end

當(dāng)Car被注射器初始化完成之后疯汁,會(huì)調(diào)用- awakeFromObjection方法,這里可以額外賦一些值

- (void)awakeFromObjection 
{
    self.test = @"111";
}

上面的說(shuō)的都是直接init出來(lái)的對(duì)象卵酪,但是更多情況下我們需要指定構(gòu)造方法

@implementation Car
objection_initializer_sel(@selector(initWithObject:)) // 該宏只需且只能出現(xiàn)一次

- (instancetype)initWithObject:(id)object
{
    if (self = [super init]) {
        self.test = object;
    }
    return self;
}
@end

取出的時(shí)候加上argumentList:參數(shù)即可

 Car *car = [[JSObjection defaultInjector] getObject:[Car class] argumentList:@[@"aaaa"]];

或者不想寫objection_initializer_sel()宏的話
可以直接在取的方法那里改動(dòng)一下變成

Car *car = [[JSObjection defaultInjector] getObject:[Car class] initializer:@selector(initWithObject:) argumentList:@[@"aaaa"]];

效果也是一樣的

對(duì)象工廠

在Car中添加一個(gè)對(duì)象工廠屬性

@property(nonatomic, strong) JSObjectFactory *objectFactory;

然后標(biāo)記注入里面加多一個(gè)objectFactory

objection_requires(@"engine", @"breaks",@"objectFactory")

然后你就可以通過(guò)

id obj = [self.objectFactory getObject:[Engine class]];

獲取到對(duì)應(yīng)的對(duì)象

模塊

你可以創(chuàng)建一個(gè)繼承自JSObjectionModule的模塊幌蚊,在里面綁定相對(duì)應(yīng)的事物,便可直接取到對(duì)應(yīng)的值

例如 一個(gè)協(xié)議和一個(gè)模塊類溃卡,對(duì)象綁定了類名和這個(gè)類所遵循的協(xié)議


@protocol APIService <NSObject>

- (void)api:(NSString *)params;

@end


@interface ModuleA : JSObjectionModule

@end

@implementation ModuleA

- (void)configure
{
    [self bindClass:[MyAPIService class] toProtocol:@protocol(APIService)];
}

@end

這時(shí)候注射器初始化方式改為

JSObjectionInjector *injectorA = [JSObjection createInjector:ModuleA.new]; [JSObjection setDefaultInjector:injectorA];

你就可以直接拿到對(duì)應(yīng)遵循了這個(gè)協(xié)議的對(duì)象而不用通過(guò)ModuleA的實(shí)例對(duì)象

MyAPIService *delegate = [injectorA getObject:@protocol(APIService)];

注意由于綁定的時(shí)候是用了bindClass:方法溢豆,所以每次取出都是不同的對(duì)象

除了綁定對(duì)象類名和協(xié)議外,還可以綁定一個(gè)對(duì)象和綁定一個(gè)類名

@implementation ModuleA

- (void)configure
{
    [self bind:對(duì)象實(shí)例 toClass:[UIApplication class]];
    [self bind:對(duì)象實(shí)例 toProtocol:@protocol(UIApplicationDelegate)];
}

@end

**注意由于綁定的時(shí)候是用了bind:方法瘸羡,所以每次取出都是相同的對(duì)象 **

當(dāng)對(duì)象被創(chuàng)建的時(shí)候漩仙,可以通過(guò)bindBlock:方法進(jìn)行干涉

@implementation ModuleA

- (void)configure
{
    [self bindClass:[MyAPIService class] toProtocol:@protocol(APIService)];
    [self bindBlock:^id(JSObjectionInjector *context) {
        MyAPIService *service = [[MyAPIService alloc] init];
        service.buildByMySelf = YES;
        return service;
    } toClass:[MyAPIService class]];
}

@end

上面這個(gè)例子表示MyAPIService被實(shí)例化后都會(huì)帶上buildByMySelf = YES

但是用這種方法的話,假如用注射器取出對(duì)象的時(shí)候帶上了參數(shù)犹赖,那我們就沒(méi)辦法拿到參數(shù)了队他,所以我們需要用到ObjectionProvider協(xié)議

@interface ProviderA : JSObjectionModule<JSObjectionProvider>

@end

@implementation ProviderA
- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments
{
    MyAPIService *service = [[MyAPIService alloc] init];
    service.buildByProvider = YES;
    service.arguments = arguments;
    return service;
}

- (void)configure
{
    [self bindProvider:[ModuleA new] toClass:MyAPIService.class];
}

@end

這樣子就能手動(dòng)構(gòu)建對(duì)象并且得到參數(shù)了

作用域

上面提及的bindClass:bindBlock:峻村、bindProvider:這些方法麸折,都有一個(gè)拓展參數(shù)inScope:(JSObjectionScope)scope;

比如:

[self bindClass:[MyAPIService class] toProtocol:@protocol(APIService) inScope:JSObjectionScopeSingleton named:@""];

[self bindBlock:^id(JSObjectionInjector *context) {
    MyAPIService *service = [[MyAPIService alloc] init];
    service.buildByMySelf = YES;
    return service;
} toClass:[MyAPIService class] inScope:JSObjectionScopeSingleton named:@""];

[self bindProvider:[ModuleA new] toClass:MyAPIService.class inScope:JSObjectionScopeSingleton];

JSObjectionScopeSingleton意味著注射器取出來(lái)的都是同個(gè)對(duì)象,
JSObjectionScopeNormal意味著注射器取出來(lái)的是不同對(duì)象粘昨。

總結(jié)

Objection 幫助你實(shí)現(xiàn)** 依賴注入 **垢啼,你只需要完成兩件事情窜锯,配置依賴關(guān)系和獲取依賴對(duì)象。配置依賴關(guān)系時(shí)芭析,你可以使用幾個(gè)常用的宏來(lái)快速的完成依賴關(guān)系的配置锚扎,另外你還可以使用模塊的概念來(lái)完成更多的綁定操作,它允許你將某些類或某些協(xié)議接口綁定到某個(gè)或某一類的對(duì)象上馁启,在配置完成后驾孔,你就可以使用關(guān)鍵的 injector 注入器獲取到你所需要的對(duì)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末进统,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浪听,更是在濱河造成了極大的恐慌螟碎,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件迹栓,死亡現(xiàn)場(chǎng)離奇詭異掉分,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)克伊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門酥郭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人愿吹,你說(shuō)我怎么就攤上這事不从。” “怎么了犁跪?”我有些...
    開封第一講書人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵椿息,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我坷衍,道長(zhǎng)寝优,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任枫耳,我火速辦了婚禮乏矾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘迁杨。我一直安慰自己钻心,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開白布铅协。 她就那樣靜靜地躺著扔役,像睡著了一般。 火紅的嫁衣襯著肌膚如雪警医。 梳的紋絲不亂的頭發(fā)上亿胸,一...
    開封第一講書人閱讀 51,258評(píng)論 1 300
  • 那天坯钦,我揣著相機(jī)與錄音,去河邊找鬼侈玄。 笑死婉刀,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的序仙。 我是一名探鬼主播突颊,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼潘悼!你這毒婦竟也來(lái)了律秃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤治唤,失蹤者是張志新(化名)和其女友劉穎棒动,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宾添,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡船惨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了缕陕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片粱锐。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扛邑,靈堂內(nèi)的尸體忽然破棺而出怜浅,到底是詐尸還是另有隱情,我是刑警寧澤蔬崩,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布海雪,位于F島的核電站,受9級(jí)特大地震影響舱殿,放射性物質(zhì)發(fā)生泄漏奥裸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一沪袭、第九天 我趴在偏房一處隱蔽的房頂上張望湾宙。 院中可真熱鬧,春花似錦冈绊、人聲如沸侠鳄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伟恶。三九已至,卻和暖如春毅该,著一層夾襖步出監(jiān)牢的瞬間博秫,已是汗流浹背潦牛。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挡育,地道東北人巴碗。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像即寒,于是被迫代替她去往敵國(guó)和親橡淆。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354