前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Objection, 一个轻量级的Objective-C依赖注入框架

Objection, 一个轻量级的Objective-C依赖注入框架

作者头像
ios122
发布2018-01-02 19:01:13
1.4K0
发布2018-01-02 19:01:13
举报
文章被收录于专栏:iOS122-移动混合开发研究院

简介

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

特点

  • "Annotation" 基于依赖注入.
  • 无缝支持自定义集成和依赖扩展.
  • 自定义绑定时类的创建方式.
  • 元类绑定.
  • 协议绑定.
  • 实例对象绑定.
  • 别名绑定.
  • 懒加载.
  • 及早计算的单例.
  • 自定义初始化方式.
  • 自定义参数和默认值.

系统要求

  • MacOS X 10.8 +
  • iOS 7.0 +

使用CocoaPods安装

注意podfile中需要指明Objection的版本号,否则无法安装成功.

代码语言:javascript
复制
pod 'Objection', '1.6.1' # 依赖注入.

然后在需要的地方导入即可头文件即可:

代码语言:javascript
复制
#import <Objection/Objection.h>

使用 Objection

基础用法

一个类可以使用宏 objection_register(可选)或 objection_register_singleton 注册到 objection. objection_requires 宏用来声明objection应该为此类的所有实例提供的依赖.objection_requires在类的继承体系中也可以安全使用.

  • objection_requires 宏,仅在从从注射器中获取类的实例时,才有意义.从注射器中获取类实例的方法,下面会具体讨论.
  • objection_requires 宏声明依赖后,使用注射器来获取此类实例时,会自动创建依赖类的实例,并赋值给响应的属性.
  • 如果使用 objection_register_singleton 宏注册一个类,并坚持使用注射器来获取此类的实例,那此类就不用自己实现单例机制了.
示例.
代码语言:javascript
复制
@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

使用选择器定义依赖.

你也可以使用选择器来定义依赖.如果给定的选择器在当前作用域看不见或无法找到,编译器会产生一个警告.

示例
代码语言:javascript
复制
@implementation Car
objection_requires_sel(@selector(engine), @selector(brakes))
@synthesize engine, brakes, awake;
@end

从Objection中获取对象.

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

代码语言:javascript
复制
- (void)someMethod {
  JSObjectionInjector *injector = [JSObjection createInjector];
  id car = [injector getObject:[Car class]];
}

一个给Objection设置一个默认的注射器.这个设置器可以在你的应用或库内,全局可用.

代码语言:javascript
复制
- (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:方法来实现:即使不使用注射器也能保证依赖被满足.

代码语言:javascript
复制
@implementation JSTableModel
objection_requires(@"RESTClient")
- (void)awakeFromNib {
  [[JSObjection defaultInjector] injectDependencies:self];
}
@end
下标操作

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

代码语言:javascript
复制
- (void)someMethod {
  JSObjectionInjector *injector = [JSObjection createInjector];
  id car = injector[[Car class]];
}

从Objection中创建的对象.

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

示例
代码语言:javascript
复制
@implementation Car
//...
objection_register_singleton(Car)
  - (void)awakeFromObjection {
    self.awake = YES;
  }
@end  

对象工厂

一个对象可以通过对象工厂来从注射器上下文来获取对象.

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

示例
代码语言:javascript
复制
@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的类到一个协议.
示例
代码语言:javascript
复制
@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知道如何绑定元类到注射器的对象上下文.

示例
代码语言:javascript
复制
@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方法在此类的实例通过注射器创建完成后,不会再被调用.

示例
代码语言:javascript
复制
@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.

示例
代码语言:javascript
复制
@implementation MyAppModule
- (void)configure {
    [self bindClass:[Singleton class] inScope:JSObjectionScopeNormal];
    [self bindClass:[Car class] inScope:JSObjectionScopeSingleton];
}
@end

别名绑定

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

示例
代码语言:javascript
复制
@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

代码语言:javascript
复制
@implementation MyAppModule
- (void)configure {
  [self registerEagerSingleton:[Car class]];
}

@end

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

一个新的注射器可以使用 withModule: 方法从一个已经存在的注射器创建.这个新的注射器将会和派生它的注射器拥有同样的绑定信息.

与之相反,如果使用 withoutModuleOfType:,新注射器就不会包含被标记为不包含的模块.

示例
代码语言:javascript
复制
injector = [otherInjector withModule:[[Level18Module alloc] init]] 
                          withoutModuleOfType:[Level17Module class]];

初始化

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

默认参数示例

代码语言:javascript
复制
@implementation ViewController
objection_initializer(initWithNibName:bundle:, @"ViewController")
@end

自定义参数示例

代码语言:javascript
复制
@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);
}

类方法初始化

代码语言:javascript
复制
@implementation Truck
objection_requires(@"engine", @"brakes")
objection_initializer(truckWithMake:model:)
+ (instancetype)truckWithMake:(NSString *) make model: (NSString *)model {
  ...
}
@end

专用初始化方法

代码语言:javascript
复制
@implementation ConfigurableCar
- (instancetype) initWithModel:(NSString *)model {
    //....
}
@end

- (void)buildCar {
  ConfigurableCar *car = [self.objectFactory getObject:[ConfigurableCar class], 
                                           initializer: @selector(initWithModel:) 
                                           withArgumentList:@[@"Passat"]];
}

测试

如果你正在使用 Kiwi 来进行应用的测试, 请检出MSSpec.它提供了一种把虚拟数据注入到你的测试标准中的便利方式.

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2015-11-08 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 特点
  • 系统要求
  • 使用CocoaPods安装
  • 使用 Objection
    • 基础用法
      • 示例.
    • 使用选择器定义依赖.
      • 示例
    • 从Objection中获取对象.
      • 依赖注入
      • 下标操作
    • 从Objection中创建的对象.
      • 示例
    • 对象工厂
      • 示例
  • 模块
    • 实例和协议的绑定
      • 示例
    • 元类的绑定
      • 示例
    • 提供者
      • 示例
    • 作用域
      • 示例
    • 别名绑定
      • 示例
    • 及早初始化的单例
      • Example
        • 从一个已经存在的注射器派生一个新的注射器
          • 示例
      • 初始化
        • 默认参数示例
          • 自定义参数示例
            • 类方法初始化
              • 专用初始化方法
              • 测试
              相关产品与服务
              数据库
              云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档