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 注入器获取到你所须要的对象。

相关文章
相关标签/搜索