iOS 依賴注入與Objection

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

固然這是最簡單的依賴注入,沒法知足咱們複雜的需求,因此有時候咱們須要使用第三方框架,如ObjectionTyphoonspa

Objection

接下來講明一下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 注入器獲取到你所須要的對象。

相關文章
相關標籤/搜索