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í)候我們需要使用第三方框架拜银,如Objection
和Typhoon
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ì)象。