Created by Linxi 2020/05/01api
首先先說明什麼叫作依賴注入bash
好比AController跳轉到BController,那麼這時候BController就須要在AController內部進行實例化,以下框架
@implementation AController : UIViewController
...
- (void)jump
{
BController *bController = [[BController alloc] init];
[self.navigationController pushViewController:bController animated:YES];
}
@end
複製代碼
這麼作的話,當AController被封裝成組件以後,BController的配置將會被限制,外部沒法改變BController任何細節,因此咱們 ** 稍 加 改 進 **ide
@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的建立經過Block暴露出來,AController內部不關心BController是如何被建立的,那麼AController對BController的依賴將經過外部的Block進行注入。ui
這,就是依賴注入。atom
固然這是最簡單的依賴注入,沒法知足咱們複雜的需求,因此有時候咱們須要使用第三方框架,如Objection
和Typhoon
spa
接下來講明一下Objection的使用code
Objection 是一個依賴注入框架,可以在你獲取一個類的實例的時候,這個類內部的屬性也同時會被實例化。舉個例子:orm
//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
複製代碼
建立一個默認注射器對象
JSObjectionInjector *injector = [JSObjection createInjector];
[JSObjection setDefaultInjector:injector];
複製代碼
實例化Car對象
Car *car = [[JSObjection defaultInjector] getObject:[Car class]];
複製代碼
這時候所依賴的engine
對象和breaks
對象都會經過init
方法實例化
最後打印屬性
car <Car: 0x6000006d8480> engine <Engine: 0x6000004841b0> breaks <Break: 0x6000004841e0>
複製代碼
假如說Car對象不能經過init
或者initWithXXX
等自定義構造方法去實例化,那麼咱們須要指定方法,讓注射器在指定的方法構建依賴
@implementation Car
objection_requires(@"engine", @"breaks")
- (void)awakeFromNib {
[[JSObjection defaultInjector] injectDependencies:self];
}
@end
複製代碼
當Car被注射器初始化完成以後,會調用- awakeFromObjection
方法,這裏能夠額外賦一些值
- (void)awakeFromObjection
{
self.test = @"111";
}
複製代碼
上面的說的都是直接init出來的對象,可是更多狀況下咱們須要指定構造方法
@implementation Car
objection_initializer_sel(@selector(initWithObject:)) // 該宏只需且只能出現一次
- (instancetype)initWithObject:(id)object
{
if (self = [super init]) {
self.test = object;
}
return self;
}
@end
複製代碼
取出的時候加上argumentList:
參數便可
Car *car = [[JSObjection defaultInjector] getObject:[Car class] argumentList:@[@"aaaa"]];
複製代碼
或者不想寫objection_initializer_sel()
宏的話 能夠直接在取的方法那裏改動一下變成
Car *car = [[JSObjection defaultInjector] getObject:[Car class] initializer:@selector(initWithObject:) argumentList:@[@"aaaa"]];
複製代碼
效果也是同樣的
在Car中添加一個對象工廠屬性
@property(nonatomic, strong) JSObjectFactory *objectFactory;
複製代碼
而後標記注入裏面加多一個objectFactory
objection_requires(@"engine", @"breaks",@"objectFactory")
複製代碼
而後你就能夠經過
id obj = [self.objectFactory getObject:[Engine class]];
複製代碼
獲取到對應的對象
你能夠建立一個繼承自JSObjectionModule
的模塊,在裏面綁定相對應的事物
,即可直接取到對應的值
例如 一個協議和一個模塊類,對象綁定了類名和這個類所遵循的協議
@protocol APIService <NSObject>
- (void)api:(NSString *)params;
@end
@interface ModuleA : JSObjectionModule
@end
@implementation ModuleA
- (void)configure
{
[self bindClass:[MyAPIService class] toProtocol:@protocol(APIService)];
}
@end
複製代碼
這時候注射器初始化方式改成
JSObjectionInjector *injectorA = [JSObjection createInjector:ModuleA.new]; [JSObjection setDefaultInjector:injectorA];
複製代碼
你就能夠直接拿到對應遵循了這個協議的對象而不用經過ModuleA的實例對象
MyAPIService *delegate = [injectorA getObject:@protocol(APIService)];
複製代碼
注意因爲綁定的時候是用了bindClass:方法,因此每次取出都是不一樣的對象
除了綁定對象類名和協議外,還能夠綁定一個對象和綁定一個類名
@implementation ModuleA
- (void)configure
{
[self bind:對象實例 toClass:[UIApplication class]];
[self bind:對象實例 toProtocol:@protocol(UIApplicationDelegate)];
}
@end
複製代碼
**注意因爲綁定的時候是用了bind:方法,因此每次取出都是相同的對象 **
當對象被建立的時候,能夠經過bindBlock:方法進行干涉
@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
複製代碼
上面這個例子表示MyAPIService被實例化後都會帶上buildByMySelf = YES
可是用這種方法的話,假如用注射器取出對象的時候帶上了參數,那咱們就沒辦法拿到參數了,因此咱們須要用到ObjectionProvider
協議
@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
複製代碼
這樣子就能手動構建對象而且獲得參數了
上面說起的bindClass:
、bindBlock:
、bindProvider:
這些方法,都有一個拓展參數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
意味着注射器取出來的都是同個對象, JSObjectionScopeNormal
意味着注射器取出來的是不一樣對象。
Objection 幫助你實現** 依賴注入 **,你只須要完成兩件事情,配置依賴關係和獲取依賴對象。配置依賴關係時,你可使用幾個經常使用的宏來快速的完成依賴關係的配置,另外你還可使用模塊的概念來完成更多的綁定操做,它容許你將某些類或某些協議接口綁定到某個或某一類的對象上,在配置完成後,你就可使用關鍵的 injector 注入器獲取到你所須要的對象。