源码解读——Resolver

Resolver 是一个采用 Swift 编写的轻量级依赖注入/服务定位框架。关于依赖注入和服务定位的概念,可以阅读 《控制反转、依赖注入、服务定位》 一文。本文源码解读的 Resolver 版本是 1.5.0。

基本原理

Resolver 是一个依赖注入/服务定位框架,但是它的核心逻辑主要还是服务定位的典型设计实现。服务定位的设计通常包括两个主要步骤:

  • 服务注册(Service Register)
  • 服务解析(Service Resolve)

服务注册通常是注册一个工厂方法,工厂方法能够根据相关参数可以实例化一个服务对象。由于允许注册多个服务,因此会有一个容器(Container)来存储这些工厂方法。通常情况下,会使用一个哈希表存储这些工厂方法,其中以服务的名称为键,以对应的工厂方法为值。这里我们暂且称这个哈希表为 注册项哈希表。

服务解析是指根据服务的名称在容器中查找对应的工厂方法,执行并返回一个服务对象的过程。通常情况下,会根据不同的需求采用不同的解析策略。比如:在某些场景下,每次都调用工厂方法初始化一个服务对象;在某些场景下,对于同一种服务只初始化一次,后续重复使用该服务对象。对于后一种情况,一般会使用另一个哈希表存储这些服务对象,其中以服务的名称为键,以对应的服务对象为值。这里我们暂且称这个哈希表为 缓存哈希表。

整体架构

在了解了服务定位的设计原理之后,我们再来看看 Resolver 的设计实现。下图所示为 Resolver 的类图,其中设计的核心在于红色虚线框中的部分,这里主要涉及到 3 个类:Resolver、ResolverRegistration、ResolverScope。这 3 个类基本覆盖了 Resolver 的主体逻辑,下面我们依次进行介绍。

ResolverRegistration

ResolverRegistration 从命名上看就知道是用来表示一个 注册项。如下所示,是 ResolverRegistration 的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ResolverRegistration<Service>: ResolverOptions<Service> {
public var key: Int
public var cacheKey: String

public init(resolver: Resolver, key: Int, name: Resolver.Name?) {
self.key = key
if let namedService = name {
self.cacheKey = String(key) + ":" + namedService.rawValue
} else {
self.cacheKey = String(key)
}
super.init(resolver: resolver)
}

public func resolve(resolver: Resolver, args: Any?) -> Service? {
fatalError("abstract function")
}
}

按照基本原理一节中的介绍,ResolverRegistration 按道理应该会定义工厂方法,但是这里我们只看到了关于 key 和 cacacheKey 的定义,这是什么情况?事实上,我们从 resolve(resolver: Resolver, args: Any?) -> Service? 方法的定义可以确定 ResolverRegistration 是一个抽象类。因此,我们需要再来看看它的子类。

ResolverRegistration 定义了三个子类,分别是:

  • ResolverRegistrationOnly
  • ResolverRegistrationResolver
  • ResolverRegistrationArgumentsN

这三个子类的定义分别如下所示,可以发现,子类中定义了工厂方法。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/// ResolverRegistration stores a service definition and its factory closure.
public final class ResolverRegistrationOnly<Service>: ResolverRegistration<Service> {

public var factory: ResolverFactory<Service>

public init(resolver: Resolver, key: Int, name: Resolver.Name?, factory: @escaping ResolverFactory<Service>) {
self.factory = factory
super.init(resolver: resolver, key: key, name: name)
}

public final override func resolve(resolver: Resolver, args: Any?) -> Service? {
guard let service = factory() else {
return nil
}
mutate(service, resolver: resolver, args: args)
return service
}
}

/// ResolverRegistrationResolver stores a service definition and its factory closure.
public final class ResolverRegistrationResolver<Service>: ResolverRegistration<Service> {

public var factory: ResolverFactoryResolver<Service>

public init(resolver: Resolver, key: Int, name: Resolver.Name?, factory: @escaping ResolverFactoryResolver<Service>) {
self.factory = factory
super.init(resolver: resolver, key: key, name: name)
}

public final override func resolve(resolver: Resolver, args: Any?) -> Service? {
guard let service = factory(resolver) else {
return nil
}
mutate(service, resolver: resolver, args: args)
return service
}
}

/// ResolverRegistrationArguments stores a service definition and its factory closure.
public final class ResolverRegistrationArgumentsN<Service>: ResolverRegistration<Service> {

public var factory: ResolverFactoryArgumentsN<Service>

public init(resolver: Resolver, key: Int, name: Resolver.Name?, factory: @escaping ResolverFactoryArgumentsN<Service>) {
self.factory = factory
super.init(resolver: resolver, key: key, name: name)
}

public final override func resolve(resolver: Resolver, args: Any?) -> Service? {
guard let service = factory(resolver, Resolver.Args(args)) else {
return nil
}
mutate(service, resolver: resolver, args: args)
return service
}
}

此外,我们还发现,这三个类的内部逻辑几乎一模一样,唯一的区别在于工厂方法的类型不一样,分别是:

  • ResolverFactory<Service>
  • ResolverFactoryResolver<Service>
  • ResolverFactoryArgumentsN<Service>

这三种工厂的定义分别如下,可以看出它们都是用于初始化服务的工厂方法,区别在于服务的初始化入参不同,有些需要参数,有些不需要,有些甚至连 Resolver 都不需要。

1
2
3
public typealias ResolverFactory<Service> = () -> Service?
public typealias ResolverFactoryResolver<Service> = (_ resolver: Resolver) -> Service?
public typealias ResolverFactoryArgumentsN<Service> = (_ resolver: Resolver, _ args: Resolver.Args) -> Service?

从这三个类对于 resolve 的实现来看可以发现,它们才是服务初始化的真正执行者。

看完 ResolverRegistration 的子类后,我们再来看它的父类 ResolverOptions,具体如下所示:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class ResolverOptions<Service> {

// MARK: - Parameters

public var scope: ResolverScope

fileprivate var mutator: ResolverFactoryMutator<Service>?
fileprivate var mutatorWithArgumentsN: ResolverFactoryMutatorArgumentsN<Service>?
fileprivate weak var resolver: Resolver?

// MARK: - Lifecycle

public init(resolver: Resolver) {
self.resolver = resolver
self.scope = Resolver.defaultScope
}

@discardableResult
public final func implements<Protocol>(_ type: Protocol.Type, name: Resolver.Name? = nil) -> ResolverOptions<Service> {
resolver?.register(type.self, name: name) { r, _ in r.resolve(Service.self) as? Protocol }
return self
}

@discardableResult
public final func resolveProperties(_ block: @escaping ResolverFactoryMutator<Service>) -> ResolverOptions<Service> {
mutator = block
return self
}

@discardableResult
public final func resolveProperties(_ block: @escaping ResolverFactoryMutatorArgumentsN<Service>) -> ResolverOptions<Service> {
mutatorWithArgumentsN = block
return self
}

@discardableResult
public final func scope(_ scope: ResolverScope) -> ResolverOptions<Service> {
self.scope = scope
return self
}

fileprivate func mutate(_ service: Service, resolver: Resolver, args: Any?) {
self.mutator?(resolver, service)
if let mutatorWithArgumentsN = mutatorWithArgumentsN {
mutatorWithArgumentsN(resolver, service, Resolver.Args(args))
}
}
}

从 ResolverOptions 的属性上看,在结合 ResolverRegistration 及其子类的定义,我们可以知道一个注册项的定义大致包含如下这些内容:

  • factory: 工厂方法,用于初始化服务对象,可以有三种工厂方法。
  • key:用于服务注册的注册项哈希表中的键。
  • cacheKey:用于服务解析的缓存哈希表中的键。
  • resolver:Resolver 类型,它是服务注册的核心实现,内部维护一个注册项哈希表,同时也是与用户打交道的类,可以认为是 Service Locator。
  • scope:ResolverScope 类型,它是服务解析的核心实现,内部维护一个缓存哈希表。
  • mutator: ResolverFactoryMutator<Service>? 类型,允许用户对初始化后的服务对象进行额外的修改。
  • mutator:ResolverFactoryMutatorArgumentsN<Service>? 类型,允许用户使用参数对初始化后的服务对象进行额外的修改。

ResolverScope

在上一节中,我们提到了 ResolverScope 主要负责服务解析,此外内部维护了一个缓存哈希表。下面,我们来深入分析一下,如下所示为 ResolverScope 的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class ResolverScope: ResolverScopeType {

// Moved definitions to ResolverScope to allow for dot notation access

/// All application scoped services exist for lifetime of the app. (e.g Singletons)
public static let application = ResolverScopeCache()
/// Cached services exist for lifetime of the app or until their cache is reset.
public static let cached = ResolverScopeCache()
/// Graph services are initialized once and only once during a given resolution cycle. This is the default scope.
public static let graph = ResolverScopeGraph()
/// Shared services persist while strong references to them exist. They're then deallocated until the next resolve.
public static let shared = ResolverScopeShare()
/// Unique services are created and initialized each and every time they're resolved.
public static let unique = ResolverScopeUnique()

// abstract base for class never called
public func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
fatalError("abstract")
}
}

从 resolve 的实现来看,我们也能够确认 ResolverScope 是一个抽象类。除此之外,它还定义了几个静态变量。这几个静态变量的类型都是 ResolverScope 的子类,而且类型有所不同。

下面,我们来看看它的子类。

ResolverScopeCache

ResolverScopeCache 的定义如下,其内部维护了一个缓存哈希表 cacahedServices。另外,它定义了一套自己的服务解析策略,从 resolve 方法可以看出大致的策略如下:

  1. 根据注册项的 cacheKey 查找服务对象,如果查到,则直接返回对应的服务对象;否则,进入步骤 2
  2. 调用服务初始化的真正执行者 ResolverRegistration 的 resolve 方法进行初始化服务对象。
  3. 如果初始化完成,则加入缓存哈希表进行缓存。
  4. 最后,返回服务对象。

此外,ResolverScopeCache 还提供一个 reset 方法,可以清空缓存哈希表。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// Cached services exist for lifetime of the app or until their cache is reset.
public class ResolverScopeCache: ResolverScope {

public override init() {}

public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
// 根据注册项的 cachedKey 读取缓存
if let service = cachedServices[registration.cacheKey] as? Service {
return service
}
// 如果没有缓存,则解析并实例化 sevice,并存入缓存
let service = registration.resolve(resolver: resolver, args: args)
if let service = service {
cachedServices[registration.cacheKey] = service
}
return service
}

public final func reset() {
cachedServices.removeAll()
}

fileprivate var cachedServices = [String : Any](minimumCapacity: 32)
}

ResolverScopeGraph

ResolverScopeGraph 的定义如下所示,它也持有一个缓存哈希表,其服务解析策略与 ResolverScopeCache 大同小异,区别在于:它在一次解析周期中对于相同的服务只解析一次,当一次解析周期完毕后,自动清空缓存哈希表。

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
/// Graph services are initialized once and only once during a given resolution cycle. This is the default scope.
public final class ResolverScopeGraph: ResolverScope {

public override init() {}

public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
// 根据注册项的 cachedKey 读取缓存
if let service = graph[registration.cacheKey] as? Service {
return service
}
// 如果没有缓存,则解析并实例化 sevice,并存入缓存
resolutionDepth = resolutionDepth + 1
let service = registration.resolve(resolver: resolver, args: args)
resolutionDepth = resolutionDepth - 1
if resolutionDepth == 0 {
graph.removeAll()
} else if let service = service, type(of: service as Any) is AnyClass {
graph[registration.cacheKey] = service
}
return service
}

private var graph = [String : Any?](minimumCapacity: 32)
private var resolutionDepth: Int = 0
}

ResolverScopeShare

ResolverScopeShare 的定义如下所示,其内部同样持有一个缓存哈希表,它的解析策略也是优先查找缓存哈希表,如果找不到,则初始化服务对象并存入缓存。不过它通过一个封装类型弱引用了服务对象,从而允许服务对象被共享。

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
/// Shared services persist while strong references to them exist. They're then deallocated until the next resolve.
public final class ResolverScopeShare: ResolverScope {

public override init() {}

public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
if let service = cachedServices[registration.cacheKey]?.service as? Service {
return service
}
let service = registration.resolve(resolver: resolver, args: args)
if let service = service, type(of: service as Any) is AnyClass {
cachedServices[registration.cacheKey] = BoxWeak(service: service as AnyObject)
}
return service
}

public final func reset() {
cachedServices.removeAll()
}

private struct BoxWeak {
weak var service: AnyObject?
}

private var cachedServices = [String : BoxWeak](minimumCapacity: 32)
}

ResolverScopeUnique

ResolverScopeUnique 的定义如下所示,它内部并没有缓存哈希表,因此每一次进行服务解析时,它都会重新初始化一个服务对象。

1
2
3
4
5
6
7
8
9
/// Unique services are created and initialized each and every time they're resolved.
public final class ResolverScopeUnique: ResolverScope {

public override init() {}
public final override func resolve<Service>(resolver: Resolver, registration: ResolverRegistration<Service>, args: Any?) -> Service? {
return registration.resolve(resolver: resolver, args: args)
}

}

ResolverScopeContainer

ResolverScopeContainer 的定义如下所示,它内部没有缓存哈希表,它的解析策略是让 Resolver 的 ResolverScopeCache 来代理执行服务解析。

1
2
3
4
5
6
7
8
9
/// Proxy to container's scope. Cache type depends on type supplied to container (default .cache)
public final class ResolverScopeContainer: ResolverScope {

public override init() {}

public override final func resolve<Service>(registration: ResolverRegistration<Service>, resolver: Resolver, args: Any?) -> Service? {
return resolver.cache.resolve(registration: registration, resolver: resolver, args: args)
}
}

Resolver

Resolver 是服务注册的核心,也是与用户直接打交道的类,用户通过调用它提供的方法进行注册和解析。

下面,我们来看一下 Resolver 的实现,由于它的代码较多,这里进行了部分删减,具体如下所示。

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

public final class Resolver {

// MARK: - Defaults

/// Default registry used by the static Registration functions.
public static var main: Resolver = Resolver()
/// Default registry used by the static Resolution functions and by the Resolving protocol.
public static var root: Resolver = main
/// Default scope applied when registering new objects.
public static var defaultScope: ResolverScope = .graph

// MARK: - Lifecycle

public init(parent: Resolver? = nil) {
self.parent = parent
}

...

@discardableResult
public final func register<Service>(_ type: Service.Type = Service.self, name: Resolver.Name? = nil,
factory: @escaping ResolverFactory<Service>) -> ResolverOptions<Service> {
lock.lock()
defer { lock.unlock() }
let key = ObjectIdentifier(Service.self).hashValue
let registration = ResolverRegistrationOnly(resolver: self, key: key, name: name, factory: factory)
add(registration: registration, with: key, name: name)
return registration
}

...

public final func resolve<Service>(_ type: Service.Type = Service.self, name: Resolver.Name? = nil, args: Any? = nil) -> Service {
lock.lock()
defer { lock.unlock() }
registrationCheck()
if let registration = lookup(type, name: name),
let service = registration.scope.resolve(resolver: self, registration: registration, args: args) {
return service
}
fatalError("RESOLVER: '\(Service.self):\(name?.rawValue ?? "NONAME")' not resolved. To disambiguate optionals use resolver.optional().")
}

...

// MARK: - Internal

/// Internal function searches the current and parent registries for a ResolverRegistration<Service> that matches
/// the supplied type and name.
private final func lookup<Service>(_ type: Service.Type, name: Resolver.Name?) -> ResolverRegistration<Service>? {
let key = ObjectIdentifier(Service.self).hashValue
let containerName = name?.rawValue ?? NONAME
if let container = registrations[key], let registration = container[containerName] {
return registration as? ResolverRegistration<Service>
}
if let parent = parent, let registration = parent.lookup(type, name: name) {
return registration
}
return nil
}

/// Internal function adds a new registration to the proper container.
private final func add<Service>(registration: ResolverRegistration<Service>, with key: Int, name: Resolver.Name?) {
if var container = registrations[key] {
container[name?.rawValue ?? NONAME] = registration
registrations[key] = container
} else {
registrations[key] = [name?.rawValue ?? NONAME : registration]
}
}

private let NONAME = "*"
private let parent: Resolver?
private let lock = Resolver.lock
private var registrations = [Int : [String : Any]]()
}

从源码中可以看出,Resolver 内部维护了一个注册项哈希表 registrations。

当用户调用 register 方法进行服务注册时,首先会在内部初始化一个注册项( ResolverRegistration 的子类)。然后,调用 add 方法将注册项存入注册项哈希表中。

当用户调用 resolve 方法进行服务解析时,首先会调用 loopup 方法查找注册项哈希表中对应的注册项,然后调用注册项对应的 scope 进行服务解析,本质上服务的初始化最终还是由注册项完成。

在 Resolver 中,默认使用的 ResolverScope 是 ResolverScopeGraph 类型,当然我们也可以在注册时,通过调用注册项的 scope 方法进行切换。

我们仔细阅读 loopup 方法和 init 方法,可以发现,Resolver 可以其实是可以构成一棵 Resolver 树,在查找注册项时,会递归地查找父级 Resolver 中的注册项哈希表。

Resolving 协议

Resolver 为服务注册和服务解析同时实现了两套方法——静态方法和实例方法。

当我们使用静态方法时,对应的 Resolver 对象是一个全局变量 root 或 main,两者其实是同一个 Resolver 对象。

当我们使用实例方法时,首先我们需要初始化一个 Resolver 对象。对此,框架提供了一个 Resolving 协议,并扩展了默认实现,如下所示。这样的话,遵循了 Resolving 协议的类会自动扩展了一个 Resolver 类型的属性,并且执行修改类型。

1
2
3
4
5
6
7
8
9
public protocol Resolving {
var resolver: Resolver { get }
}

extension Resolving {
public var resolver: Resolver {
return Resolver.root
}
}

注解注入

Resolver 除了完整实现了一套服务定位架构之外,还支持了注解注入。它提供了五种类型的注解,底层使用 propertyWrapper 实现的,分别如下:

  • Injected<Service>
  • OptionalInjected<Service>
  • LazyInjected<Service>
  • WeakLazyInjected<Service>
  • InjectedObject<Service>

使用注解的前提是,服务必须提前进行注册,因为注解在初始化时会立即进行服务解析。不过,LazyInjected<Service> 和 WeakLazyInjected<Service> 并无此要求,对于它们,只需要在调用前进行服务注册即可。

设计模式

Resolver 框架其实采用了 策略模式 进行设计实现的。

当服务注册时,Resolver 依赖的是抽象类 ResolverRegistration,框架提供了三种具体类型的 ResolverRegistration。当已有类型满足不了需求时,我们可以对 Resolver 的 register 方法进行重载,并自定义 ResolverRegistration 子类。

当服务注册时,Resolver 依赖的同样是抽象类 ResolverScope,框架提供两个五种具体类型的 ResolverScope 以提供不同的解析策略。当已有类型满足不了需求时,我们同样可以自定义 ResolverScope 子类实现定制的解析策略。

总结

本文对 Resolver 源码进行了解读,分析其整体框架的设计。Resolver 的核心主要包含三个部分:ResolverRegistration、ResolverScope、Resolver,它们各有各的职责,遵循了职责单一的设计原则,并且实现了一个扩展性很强的策略模式。

我认为 Resolver 非常适合作为重构的一套辅助工具,希望后面能够在实际开发中使用一下,从而能够在实践中提高一下自己的架构设计能力。

参考

  1. Resolver
  2. 控制反转、依赖注入、服务定位
  3. Design Pattern