Objecttion依赖注入

描述

Objection 是一个轻量级的Objective-C依赖注入框架,可同时用于MacOS X 或者iOS系统.对于那些使用过Guice(一个Java依赖注入框架)的开发者,会感觉Objection 似曾相识.Objection以一种相对容易接受的方式来使你尽可能地不需要管理一个庞大的XML容器或者手动创建对象.


特征

  • “Annotation”基于依赖注入

  • 无缝支持自定义集成和依赖拓展

    • 自定义类的创建

    • 元类绑定

    • 协议绑定

    • 实例对象绑定

    • 别名绑定

  • 懒加载

  • 及早初始化得单例

  • 自定义初始化

    ** 默认和自定义参数


准备工作

CocoaPods

  • 在Podfile文件中添加
1
pod 'Objection', '1.6.1'
  • 安装Objection
1
pod install
  • 包含头文件
1
#import <Objection/Objection.h>

##使用Objection

基本使用

一个类必须使用Objection的objection_register(可选)或者objection_register_singleton宏定义来注册。objection_register就是声明所依赖Objection创建的全部实例。

  • 使用objection_register宏,参数是类名
1
objection_register(@"类名")
  • 使用objection_requires宏,参数对象名(别名)
1
objection_requires(@"对象名")

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@class Engine, Brakes;
//
@interface Car : NSObject
{
Engine *engine;
Brakes *brakes;
BOOL awake;
}
// 将会被Objection填充
@property(nonatomic, strong) Engine *engine;
// Will be filled in by objection
@property(nonatomic, strong) Brakes *brakes;
@property(nonatomic) BOOL awake;
//
@implementation Car
// 使用objection_requires来绑定两个对象,填写属性名称即可。
objection_requires(@"engine", @"brakes")
// 告诉编译器,自定义setter和getter方法
@synthesize engine, brakes, awake;
@end

定义方法依赖

你也可以使用方法来定义依赖,如果你定义的方法不可见或者没有,那么编译器就会出现一个警告。

例子

1
2
3
4
@implementation Car
objection_requires_sel(@selector(engine), @selector(brakes))
@synthesize engine, brakes, awake;
@end

从Objection获取对象

可以创建一个注射器,然后从这个注射器中获取指定类或协议的一个实例.注射器各自管理自己的对象上下文.这意味着:Objection中的单例指的是一个注射器中只存在一个某个类的实例,并不一定是真正意义上的单例(即那种应用程序全局唯一的类的实例对象).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)someMethod {
// 创建注射器。
JSObjectionInjector *injector = [JSObjection createInjector];
// 通过类从注射器获取对象
id car = [injector getObject:[Car class]];
}
//
//
一个给Objection设置一个默认的注射器.这个设置器可以在你的应用或库内,全局可用.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 在appdelegate中创建注册器
JSObjectionInjector *injector = [JSObjection createInjector];
[JSObjection setDefaultInjector:injector];
}
//
// 其他文件
- (void)viewDidLoad {
// 可以在任何地方中使用
id myModel = [[JSObjection defaultInjector] getObject:[MyModel class]];
}

依赖注入

有可能类的实例对象并不是通过注射器创建的,此时如果不做特殊处理,依赖不会被正确处理,相关属性可能为nil.但是如果对于使用 objection_requires宏指定依赖的情况,你可以通过injectDependencies:方法来实现:即使不使用注射器也能保证依赖被满足

1
2
3
4
5
6
@implementation JSTableModel
objection_requires(@"RESTClient")
- (void)awakeFromNib {
[[JSObjection defaultInjector] injectDependencies:self];
}
@end

下标操作

Objection已经支持使用下标操作,从注射器上下文来获取对象.

1
2
3
4
5
- (void)someMethod {
JSObjectionInjector *injector = [JSObjection createInjector];
// [Car class]为下标,获取对象
id car = injector[[Car class]];
}

从Objection中创建的对象

如果一个对象需要知道它使合适被objection完全初始化的,可以实现方法awakeFromObjection .注意:对象被Objection完全初始化时会调用awakeFromObjection方法,你在这里可以加入自定义的一些操作;而awake只是一个例子中的自定义标记属性而已,并不是Objection的一部分.

1
2
3
4
5
6
7
8
9
@implementation Car
//...
//
objection_register_singleton(Car)
//
- (void)awakeFromObjection {
awake = YES;
}
@end

对象工厂

一个类可以通过对象工厂来从注射器上下文来获取对象.
自定义JSObjectFactory属性,需要使用 objection_requires 宏来指明依赖,如 objection_requires(@”objectFactory”) .这样当从注射器中获取这个类的实例时,会自动获取与此注射器相关的JSObjectFactory对象工厂实例.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@interface RequestDispatcher
@property(nonatomic, strong) JSObjectFactory *objectFactory
@end
//
//
@implementation RequestDispatcher
objection_requires(@"objectFactory")
//
// 自定义方法
- (void)dispatch:(NSDictionary *)params
{
// 通过JSObjectFactory获取对象
Request *request = [self.objectFactory getObject:[Request class]];
request.params = params;
[request send];
}
@end

模块

一个模块是一组绑定信息,它为注射器提供了额外的配置信息。它在整合外部依赖和绑定协议到类或实例时特别有用.

实例和协议的绑定
绑定一个协议或类到该类型指定的某个实例.

绑定一个已经注册到Objection的类到一个协议.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@interface MyAppModule : JSObjectionModule {
}
@end
//
//
@implementation MyAppModule
// 重写配置方法
- (void)configure {
// 第一种方式:绑定类和实例
[self bind:[UIApplication sharedApplication] toClass:[UIApplication class]];
//
// 第二种方式:绑定代理协议和实例
[self bind:[UIApplication sharedApplication].delegate toProtocol:@protocol(UIApplicationDelegate)];
//
// 第三种方式:绑定代理协议和类
[self bindClass:[MyAPIService class] toProtocol:@protocol(APIService)];
}
@end
//
//
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建注册器
JSObjectionInjector *injector = [JSObjection createInjector:[[MyAppModule alloc] init]];
[JSObjection setDefaultInjector:injector];
}

元类绑定

有时候,我们仅仅是想使用依赖的某个类的类方法.Objection可以通过协议显示地支持元类的绑定.这样就不用再创建一个包装类来传递类方法.要注意的是,它需要定义一个协议来让Objection知道如何绑定元类到注射器的对象上下文.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@protocol ExternalUtility  
//!< 注意此处,确实是`-`减号.通常是不支持让元类直接支持协议的.此处是以类本身作为对象,来取执行协议,而不是使用该类的某一个实例.
- (void)doSomething;
@end
//
@interface ExternalUtility
+ (void)doSomething;
@end
//
//
@implementation ExternalUtility
+ (void)doSomething {...}
@end
// Module Configuration
- (void)configure {
[self bindMetaClass:[ExternalUtility class] toProtocol:@protocol(ExternalUtility)];
}
//
//
@interface SomeClass
{
...
}
// 使用 'assign' 是因为一个元类不受通常的 retain/release声明周期限制.
// 它将会一直存在,直到应用程序终止(类初始化 -> 应用终止),不管运行时有多少指向它的对象引用.
// 在运行时引用的对象的数量无关。
@property (nonatomic, assign) id<ExternalUtility> externalUtility
@end

提供者

偶尔你可能想要在Objection内部手动构造一个对象.提供者允许你使用自定义的机制来创建某个类型的对象.你可以创建一个 遵守 ObjectionProvider 协议的对象,或者你可以使用一个 block 来创建对象.

如果使用了对像提供者,则原类中的 -awakeFromObjection方法在此类的实例通过注射器创建完成后,不会再被调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// CarProvider是一个提供类,遵守JSObjectionProvider协议
@interface CarProvider : NSObject <JSObjectionProvider>
@end
//
@implementation CarProvider
//
// 实现JSObjectionProvider方法,在这里你可以做创建对象和一些数据转化的操作,例如json转对象模型数据
- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments {
// Manually build object
// 手动创建对象
return car;
}
@end
//
@implementation MyAppModule
// 重写配置方法
- (void)configure {
[self bindProvider:[[CarProvider alloc] init] toClass:[Car class]];
[self bindBlock:^(JSObjectionInjector *context) {
// Manually build object
// 手动创建对象
return car;
} toClass:[Car class]];
}
@end

作用域

一个类被用作模块作用域内的单例.相反,一个已经注册的单例在也可以被降级为注射器上下文中一个普通声明周期的实例对象.

也就是说,你有两种方式来指定类实例在注射器上下文是单例对象还是普通对象.一种是在类实现中使用 objection_register_singleton 宏,一种是在模块配置方法中指定作用域为JSObjectionScopeSingleton.

1
2
3
4
5
6
7
8
9
10
11
@implementation MyAppModule
- (void)configure {
// 枚举值
// JSObjectionScopeNone = -1,
// JSObjectionScopeNormal, 正常
// JSObjectionScopeSingleton 单例
[self bindClass:[Singleton class] inScope:JSObjectionScopeNormal];
// 在单例范围中绑定类
[self bindClass:[Car class] inScope:JSObjectionScopeSingleton];
}
@end

别名绑定

同一个类或者协议的依赖关系可以使用objection_requires_names宏标记,这个宏使用属性别名字典作为参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@interface ShinyCar : NSObject
// 相同的类Headlight
@property (nonatomic, strong) Headlight *leftHeadlight;
@property (nonatomic, strong) Headlight *rightHeadlight;
@end
//
@implementation ShinyCar
objection_register(ShinyCar)
// 使用objection_requires_names进行注册,参数是字典形式。
objection_requires_names((@{@"LeftHeadlight":@"leftHeadlight", @"RightHeadlight":@"rightHeadlight"}))
@synthesize leftHeadlight, rightHeadlight;
@end
//
@implementation NamedModule
- (void)configure
{
// 绑定
[self bind:[[Headlight alloc]init] toClass:[Headlight class] named:@"RightHeadlight"];
[self bindClass:[HIDHeadlight class] toClass:[Headlight class] named:@"LeftHeadlight"];
}
@end

及早初始化的单例

你可以将已经注册的单例用作及早初始化的单例.及早初始化的单例,在注射器创建时创建,而不再是懒加载.

注意:如果将一个未注册为单例的类设置为及早初始化的单例,会引起崩溃.

1
2
3
4
5
6
@implementation MyAppModule
- (void)configure {
// 通过类型注册单例
[self registerEagerSingleton:[Car class]];
}
@end

从一个已经存在的注射器派生一个新的注射器

一个新的注射器可以使用 withModule: 方法从一个已经存在的注射器创建.这个新的注射器将会和派生它的注射器拥有同样的绑定信息.
与之相反,如果使用 withoutModuleOfType:,新注射器就不会包含已存在的注射器的绑定信息

1
2
3
// 从otherInjector获取新的注射器injector,其中包含Level18Module模块的绑定信息,但是不包含Level17Module的绑定信息
injector = [otherInjector withModule:[[Level18Module alloc] init]]
withoutModuleOfType:[Level17Module class]];

初始化Initializers

默认情况下,Objection 使用默认的初始化方法 init 创建对象.如果你想使用其他的初始化方法来初始化对象,可以借助 objection_initializer 宏.这个宏支持传递默认参数(暂时不支持标量参数,即基本类型参数,如数字).

  • 默认参数初始化
1
2
3
@implementation ViewController
objection_initializer(initWithNibName:bundle:, @"ViewController")
@end
  • 自定义参数初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation ConfigurableCar
objection_requires(@"engine", @"brakes")
objection_initializer(initWithMake:model:)
@synthesize make;
@synthesize model;
- (instancetype)initWithMake:(NSString *)make model:(NSString *)model {
...
}
@end
//
//
- (void)buildCar {
ConfigurableCar *car = [self.objectFactory getObjectWithArgs:[ConfigurableCar class], @"VW", @"Passat", nil];
NSLog(@"Make: %@ Model: %@", car.make, car.model);
}

类方法初始化

1
2
3
4
5
6
7
@implementation Truck
objection_requires(@"engine", @"brakes")
objection_initializer(truckWithMake:model:)
+ (instancetype)truckWithMake:(NSString *) make model: (NSString *)model {
...
}
@end

专用初始化

1
2
3
4
5
6
7
8
9
10
11
@implementation ConfigurableCar
- (instancetype) initWithModel:(NSString *)model {
//....
}
@end
//
- (void)buildCar {
ConfigurableCar *car = [self.objectFactory getObject:[ConfigurableCar class],
initializer: @selector(initWithModel:)
withArgumentList:@[@"Passat"]];
}