Objection, 一个轻量级的Objective-C依赖注入框架
简介
Objection 是一个轻量级的Objective-C依赖注入框架,可同时用于MacOS X 或者iOS.对于那些使用过Guice(一个Java依赖注入框架)的开发者,会感觉Objection 似曾相识.Objection用来以一种相对容易接受的方式来使你尽可能地不需要管理一个庞大的XML容器或者手动创建对象.
特点
- "Annotation" 基于依赖注入.
- 无缝支持自定义集成和依赖扩展.
- 自定义绑定时类的创建方式.
- 元类绑定.
- 协议绑定.
- 实例对象绑定.
- 别名绑定.
- 懒加载.
- 及早计算的单例.
- 自定义初始化方式.
- 自定义参数和默认值.
系统要求
- MacOS X 10.8 +
- iOS 7.0 +
使用CocoaPods安装
注意podfile中需要指明Objection的版本号,否则无法安装成功.
pod 'Objection', '1.6.1' # 依赖注入.
然后在需要的地方导入即可头文件即可:
#import <Objection/Objection.h>
使用 Objection
基础用法
一个类可以使用宏 objection_register(可选)或 objection_register_singleton 注册到 objection. objection_requires 宏用来声明objection应该为此类的所有实例提供的依赖.objection_requires在类的继承体系中也可以安全使用.
- objection_requires 宏,仅在从从注射器中获取类的实例时,才有意义.从注射器中获取类实例的方法,下面会具体讨论.
- objection_requires 宏声明依赖后,使用注射器来获取此类实例时,会自动创建依赖类的实例,并赋值给响应的属性.
- 如果使用 objection_register_singleton 宏注册一个类,并坚持使用注射器来获取此类的实例,那此类就不用自己实现单例机制了.
示例.
@class Engine, Brakes;
@interface Car : NSObject
// 将会通过依赖注入赋值.
@property(nonatomic, strong) Engine *engine;
// 将会通过依赖注入赋值.
@property(nonatomic, strong) Brakes *brakes;
@property(nonatomic) BOOL awake;
@implementation Car
objection_requires(@"engine", @"brakes")
@synthesize engine, brakes, awake;
@end
使用选择器定义依赖.
你也可以使用选择器来定义依赖.如果给定的选择器在当前作用域看不见或无法找到,编译器会产生一个警告.
示例
@implementation Car
objection_requires_sel(@selector(engine), @selector(brakes))
@synthesize engine, brakes, awake;
@end
从Objection中获取对象.
可以创建一个注射器,然后从这个注射器中获取指定类或协议的一个实例.注射器各自管理自己的对象上下文.这意味着:Objection中的单例指的是一个注射器中只存在一个某个类的实例,并不一定是真正意义上的单例(即那种应用程序全局唯一的类的实例对象).
- (void)someMethod {
JSObjectionInjector *injector = [JSObjection createInjector];
id car = [injector getObject:[Car class]];
}
一个给Objection设置一个默认的注射器.这个设置器可以在你的应用或库内,全局可用.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
JSObjectionInjector *injector = [JSObjection createInjector];
[JSObjection setDefaultInjector:injector];
}
- (void)viewDidLoad {
id myModel = [[JSObjection defaultInjector] getObject:[MyModel class]];
}
依赖注入
有可能类的实例对象并不是通过注射器创建的,此时如果不做特殊处理,依赖不会被正确处理,相关属性可能为nil.但是如果对于使用 objection_requires宏指定依赖的情况,你可以通过injectDependencies:方法来实现:即使不使用注射器也能保证依赖被满足.
@implementation JSTableModel
objection_requires(@"RESTClient")
- (void)awakeFromNib {
[[JSObjection defaultInjector] injectDependencies:self];
}
@end
下标操作
Objection 已经支持使用下标操作来从注射器上下文来获取对象.
- (void)someMethod {
JSObjectionInjector *injector = [JSObjection createInjector];
id car = injector[[Car class]];
}
从Objection中创建的对象.
如果一个对象需要知道它使合适被objection完全初始化的,可以实现方法awakeFromObjection .注意:对象被Objection完全初始化时会调用awakeFromObjection方法,你在这里可以加入自定义的一些操作;而awake只是一个例子中的自定义标记属性而已,并不是Objection的一部分.
示例
@implementation Car
//...
objection_register_singleton(Car)
- (void)awakeFromObjection {
self.awake = YES;
}
@end
对象工厂
一个对象可以通过对象工厂来从注射器上下文来获取对象.
自定义JSObjectFactory属性,需要使用 objection_requires 宏来指明依赖,如 objection_requires(@"objectFactory") .这样当从注射器中获取这个类的实例时,会自动获取与此注射器相关的JSObjectFactory对象工厂实例.
示例
@interface RequestDispatcher
@property(nonatomic, strong) JSObjectFactory *objectFactory
@end
@implementation RequestDispatcher
objection_requires(@"objectFactory")
- (void)dispatch:(NSDictionary *)params
{
Request *request = [self.objectFactory getObject:[Request class]];
request.params = params;
[request send];
}
@end
模块
一个模块就是一组绑定信息.这些绑定信息用来给注射器增加额外的配置信息.它在整合外部依赖和绑定协议到类或实例时特别有用.
实例和协议的绑定
- 绑定一个协议或类到该类型指定的某个实例.
- 绑定一个已经注册到Objection的类到一个协议.
示例
@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知道如何绑定元类到注射器的对象上下文.
示例
@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
方法在此类的实例通过注射器创建完成后,不会再被调用.
示例
@interface CarProvider : NSObject <JSObjectionProvider>
@end
@implementation CarProvider
- (id)provide:(JSObjectionInjector *)context arguments:(NSArray *)arguments {
// 手动创建对象的代码
return car;
}
@end
@implementation MyAppModule
- (void)configure {
[self bindProvider:[[CarProvider alloc] init] toClass:[Car class]];
[self bindBlock:^(JSObjectionInjector *context) {
// 手动创建对象.
return car;
} toClass:[Car class]];
}
@end
作用域
一个类被用作模块作用域内的单例.相反,一个已经注册的单例在也可以被降级为注射器上下文中一个普通声明周期的实例对象.
也就是说,你有两种方式来指定类实例在注射器上下文是单例对象还是普通对象.一种是在类实现中使用 objection_register_singleton 宏,一种是在模块配置方法中指定作用域为JSObjectionScopeSingleton.
示例
@implementation MyAppModule
- (void)configure {
[self bindClass:[Singleton class] inScope:JSObjectionScopeNormal];
[self bindClass:[Car class] inScope:JSObjectionScopeSingleton];
}
@end
别名绑定
同一个类或协议的依赖可以使用 objection_requires_names 宏标记,这个宏使用属性别名字典作为参数.
示例
@interface ShinyCar : NSObject
@property (nonatomic, strong) Headlight *leftHeadlight;
@property (nonatomic, strong) Headlight *rightHeadlight;
@end
@implementation ShinyCar
objection_register(ShinyCar)
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
及早初始化的单例
你可以将已经注册的单例用作及早初始化的单例.及早初始化的单例,在注射器创建时创建,而不再是懒加载.
注意:如果将一个未注册为单例的类设置为及早初始化的单例,会引起崩溃.
Example
@implementation MyAppModule
- (void)configure {
[self registerEagerSingleton:[Car class]];
}
@end
从一个已经存在的注射器派生一个新的注射器
一个新的注射器可以使用 withModule: 方法从一个已经存在的注射器创建.这个新的注射器将会和派生它的注射器拥有同样的绑定信息.
与之相反,如果使用 withoutModuleOfType:,新注射器就不会包含被标记为不包含的模块.
示例
injector = [otherInjector withModule:[[Level18Module alloc] init]]
withoutModuleOfType:[Level17Module class]];
初始化
默认地,Objection 使用默认的初始化方法 init
创建对象.如果你想使用其他的初始化方法来初始化对象,可以借助 objection_initializer
宏.这个宏支持传递默认参数(暂时不支持标量参数,即基本类型参数,如数字).
默认参数示例
@implementation ViewController
objection_initializer(initWithNibName:bundle:, @"ViewController")
@end
自定义参数示例
@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);
}
类方法初始化
@implementation Truck
objection_requires(@"engine", @"brakes")
objection_initializer(truckWithMake:model:)
+ (instancetype)truckWithMake:(NSString *) make model: (NSString *)model {
...
}
@end
专用初始化方法
@implementation ConfigurableCar
- (instancetype) initWithModel:(NSString *)model {
//....
}
@end
- (void)buildCar {
ConfigurableCar *car = [self.objectFactory getObject:[ConfigurableCar class],
initializer: @selector(initWithModel:)
withArgumentList:@[@"Passat"]];
}
测试
如果你正在使用 Kiwi 来进行应用的测试, 请检出MSSpec.它提供了一种把虚拟数据注入到你的测试标准中的便利方式.
Objection, 一个轻量级的Objective-C依赖注入框架的更多相关文章
- 如何构建一个轻量级级的DI(依赖注入)
概念:依赖注入与IOC模式类似工厂模式,是一种解决调用者和被调用者依赖耦合关系的模式:它解决了对象之间的依赖关系,使得对象只依赖IOC/DI容器,不再直接相互依赖,实现松耦合,然后在对象创建时,由IO ...
- ASP.NET Core 6框架揭秘实例演示[06]:依赖注入框架设计细节
由于依赖注入具有举足轻重的作用,所以<ASP.NET Core 6框架揭秘>的绝大部分章节都会涉及这一主题.本书第3章对.NET原生的依赖注入框架的设计和实现进行了系统的介绍,其中设计一些 ...
- 【Java】利用注解和反射实现一个"低配版"的依赖注入
在Spring中,我们可以通过 @Autowired注解的方式为一个方法中注入参数,那么这种方法背后到底发生了什么呢,这篇文章将讲述如何用Java的注解和反射实现一个“低配版”的依赖注入. 下面是我们 ...
- ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
ExpandoObject与DynamicObject的使用 using ImpromptuInterface; using System; using System.Dynamic; names ...
- [ASP.NET Core 3框架揭秘] 依赖注入[4]:一个Mini版的依赖注入框架
在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类 ...
- Autofac 依赖注入框架 使用
简介 Autofac是一款IOC框架,比较于其他的IOC框架,如Spring.NET,Unity,Castle等等所包含的,它很轻量级性能上非常高. 官方网站http://autofac.org/ 源 ...
- Spring.NET依赖注入框架学习--入门
Spring.NET依赖注入框架学习--入门 在学些Spring.net框架之前,有必要先脑补一点知识,比如什么是依赖注入?IOC又是什么?控制反转又是什么意思?它们与Spring.net又有什么关系 ...
- 史上最好用的依赖注入框架Google Guice【转】
Guice是Google开发的一个轻量级,基于Java5(主要运用泛型与注释特性)的依赖注入框架(IOC).Guice非常小而且快. (其他的依赖注入框架还有Dagger,Spring) Spring ...
- ASP.NET Core技术研究-探秘依赖注入框架
ASP.NET Core在底层内置了一个依赖注入框架,通过依赖注入的方式注册服务.提供服务.依赖注入不仅服务于ASP.NET Core自身,同时也是应用程序的服务提供者. 毫不夸张的说,ASP.NET ...
随机推荐
- inventor安装错误
AUTODESK系列软件着实令人头疼,安装失败之后不能完全卸载!!!(比如maya,cad,3dsmax等).有时手动删除注册表重装之后还是会出现各种问题,每个版本的C++Runtime和.NET f ...
- object的equals方法与“==”的使用
官方文档是这么说的:
- PHP函数库(概览)
数组函数: array_change_key_case — 返回字符串键名全为小写或大写的数组 array_chunk — 将一个数组分割成多个 array_column — 返回数组中指定的一列 a ...
- Andrew Ng 的 Machine Learning 课程学习 (week3) Logistic Regression
这学期一直在跟进 Coursera上的 Machina Learning 公开课, 老师Andrew Ng是coursera的创始人之一,Machine Learning方面的大牛.这门课程对想要了解 ...
- Supervised learning demo
监督学习案例 规范 假设函数: 使用h(hypothesis, 假设)表示 输入(input value) 向量或者实数: 使用小写字母x等 矩阵: 使用大写字母X等 输出(output value) ...
- NOPI导出execl 多个sheet,一列图片
NPOI API: http://www.cnblogs.com/atao/archive/2009/11/15/1603528.html http://blog.csdn.net/pan_junbi ...
- 关于webform textbox Password 模式
textbox在这个模式时,如果进行点击按钮或者其他与后台交互的操作,则状态不会保留,既密码框内容会被清空: 这个可以在前台使用 隐藏控件加js获取密码框内容赋值到隐藏控件,点击刷新后通过后台为密码框 ...
- 封装WebService的APM为Async、Await模式利于Asp.Net页面调用
Wcf针对Async.Await指令直接可以返回Task<T>结果,但是老旧的系统中还是会有很多是在用Soap的Webservice.直接在Asp.Net页面调用APM方法确实比较麻烦,其 ...
- 关于web.xml的welcome-file-list 配置与tomcat的关系:
关于web.xml的welcome-file-list 配置与tomcat的关系: 2018年04月18日 10:17:13 守望dfdfdf 阅读数:377 标签: welcome-file-lis ...
- maven课程 项目管理利器-maven 3-8 maven依赖传递 4星
本节主要讲了 1 maven依赖传递 本地项目路径:F:\xiangmu3\Xin\FuQiang\maven\code 2 maven排除依赖 3 注意事项 4 零散知识点 1 maven依赖传递 ...