盲点 1.3.1

盲点 1.3.1

测试已测试
Lang语言 Obj-CObjective C
许可 MIT
发布最新版本2016年4月

Andrew Kitchen 维护。



盲点 1.3.1

  • 作者:
  • JB Steadman

盲点:Objective-C 的依赖注入

为什么我应该使用这个?

盲点可以帮助您编写干净的代码。您可以保持对象松散耦合,并且对外界的知识一无所知。您的对象可以创建其他对象,而无需了解其他对象的依赖。

这个名称是什么意思?

这是一个关于对象应该对外部依赖的来源“瞎”的概念。然而,绝大多数情况下,将其命名为“BSInjector”、“BSProvider”等是非常有趣的。

我们是什么,是 Java 吗?

抱歉,是的。在语言提供的对象级依赖管理方面,Objective-C 和 Java 是相同的:基本上没有。全局变量、“singleton”单例和透传参数是常见的访问依赖模式的选项。它们都不是一个好的选择。

Guice 将优雅的对象级依赖管理带到了 Java,消除了静态数据的需求,并实现了真正的面向对象编程的承诺。盲点试图为 Objective-C 做同样的事情。

它有哪些特性?

  • 注入 UIViewController 子类的依赖项,包括主/详情视图控制器
  • 通过初始化器进行“构造函数注入”
  • 通过属性进行“设置器注入”
  • 使用分类对第三方类进行注入
  • 绑定到实例、块、提供者或类
  • 作用域绑定,包括单例
  • 支持在绑定中未定义的创建时参数。

状态

盲点是 alpha 软件版本。除了这段说明文档外,没有其他文档。我的重点现在是构建文档和示例。我在当前的 iOS 项目中使用它,并且运行良好。以这种状态发布了它,看看是否会引起任何兴趣。如果您已经看到了这段说明文档并有一些问题,或者想要了解更多关于如何使用盲点,请与我联系。

它是如何工作的?

您描述您的对象的依赖,定义填充这些依赖的绑定,然后要求 BSInjector 创建您的对象。这里有一个“Hello,World”示例

/**
 * Our view controller. It needs to be created with an api instance.
 */
@interface MyViewController : UIViewController
- (id)initWithApi:(id<MyApi>)api;
@end

@implementation MyViewController

/**
 * Describing MyViewController's dependencies. We want instances initialized using initWithApi:, which takes one arg.
 */
+ (BSInitializer *)bsInitializer {
        return [BSInitializer initializerWithClass:self 
                                          selector:@selector(initWithApi:) 
                                      argumentKeys:@"myApi", nil];
}

...

@end


@interface MyBlindsideModule : NSObject<BSModule>
@end

@implementation MyBlindsideModule

/**
 * Creating a binding for our MyApi dependency.
 */
- (void)configure:(id<BSBinder>) binder {
        id<MyApi> apiInstance = [[MyApiImpl alloc] initWithEndpoint:@"http://api.mycompany.com"];
        [binder bind:@"myApi" toInstance:apiInstance];
}
@end


@implementation AppDelegate
- (void)applicationDidFinishLaunching {
        ...
        // Creating an injector configured with our BSModule. Asking the injector for an instance of our ViewController.
        MyBlindsideModule *module = [[MyBlindsideModule alloc] init];
        id<BSInjector> injector = [Blindside injectorWithModule:module];
        UIViewController *rootViewController = [injector getInstance:[MyViewController class]];

        ...
}
@end

显然,对于这种微不足道的工作,你不需要框架。Blindside 确实在 MyViewController 创建另一个视图控制器时非常有用,而这个控制器又创建了另一个,并包含额外的依赖项,依此类推。

描述依赖关系

Blindside 通过两种方式向对象提供依赖:通过初始化器(例如:initWithDelegate:)或使用属性。您可以将这两种方法混合使用。

Blindside 需要两个类方法来描述依赖关系。这些方法被添加到 NSObject 的 NSObject+Blindside 类别中,并意在被注入 Blindside 的类所重写。这些方法是

  • (BSInitializer *)bsInitializer;
  • (BSPropertySet *)bsProperties;

bsInitializer 描述了创建类的实例时使用的初始化方法,包括初始化者的选择器和参数。Blindside 可以使用类的 BSInitializer 创建类的实例,并注入依赖项。

bsProperties 描述了将要注入到已创建的对象中的属性。

以下是一个用于名为 House 的类的两个方法实现的示例。House 类作为初始化参数接受一个 Address,并具有一个 UIColor 类型的属性。

@implementation House

+ (BSInitializer *)bsInitializer {
        SEL selector = @selector(initWithAddress:)
        return [BSInitializer initializerWithClass:self selector:selector argumentKeys:[Address class]];
}

+ (BSPropertySet *)bsProperties {
        BSPropertySet *propertySet = [BSPropertySet propertySetWithClass:self propertyNames:@"color", nil];
        [propertySet bindProperty:@"color" toKey:@"myHouseColor"];
        return propertySet;
}
 ...

从注入中唤醒

在处理属性注入时,有时希望有一个钩子,可以在所有依赖注入完成后用于完成对象的设置。Blindside 为此提供了一个机制

@implementation House
- (void)bsAwakeFromPropertyInjection {
    // Finalize instantiation
}
 ...

请注意,不推荐使用此方法,因为它会增加您的代码与 Blindside 之间的耦合。首先,在对象上寻找其他适当的生命周期方法(例如视图控制器上的 -viewDidLoad),以便可以执行此类工作。

Swift

您也可以在 Swift 中使用 Blindside。首先,将 #import <Blindside/Blindside.h> 添加到 Swift 的桥接头文件中,以便将框架暴露给 Swift 代码。

创建一个类似于 Objective-C 的注入器

let module = MyBlindsideModule()
let injector = Blindside.injectorWithModule(module)

为了无参数实例化您的控制器

let controller = injector.getInstance(MyViewController.self)

要传递动态参数(标记为 BS_DYNAMIC 的参数)到您的控制器,请使用新提供的 -getInstance:withArgArray: 方法。

let controller = injector.getInstance(MyViewController.self, withArgArray: [BSNull(), "arg"])

如此描述类的依赖关系

class House : NSObject {
    class override func bsInitializer() -> BSInitializer {
        // `selector` requires the Objective-C method name of your initializer
        return BSInitializer(withClass: self, selector: "initWithAddress:", argumentKeysArray: [Address.self])
    }

    class override func bsProperties() -> BSPropertySet {
        let propertySet = BSPropertySet(withClass: self, propertyNamesArray: ["garage"])
        propertySet.bindProperty("color", toKey: "myHouseColor")
        return propertySet
    }
}

请注意,Blindside 仅能创建和注入 NSObject 派生类。

作者

版权所有 © 2012 JB Steadman。本软件根据 MIT 许可证授权。