源码解读——Resolver

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

基本原理

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

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

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

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

整体架构

在了解了服务定位的设计原理之后,我们再来看看 Resolver 的设计实现。下图所示为 Resolver 的类图,其中设计的核心在于红色虚线框中的部分,这里主要涉及到 3 个类:ResolverResolverRegistrationResolverScope。这 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 按道理应该会定义工厂方法,但是这里我们只看到了关于 keycacacheKey 的定义,这是什么情况?事实上,我们从 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:用于服务解析的缓存哈希表中的键。
  • resolverResolver 类型,它是服务注册的核心实现,内部维护一个注册项哈希表,同时也是与用户打交道的类,可以认为是 Service Locator。
  • scopeResolverScope 类型,它是服务解析的核心实现,内部维护一个缓存哈希表。
  • mutator: ResolverFactoryMutator<Service>? 类型,允许用户对初始化后的服务对象进行额外的修改。
  • mutatorResolverFactoryMutatorArgumentsN<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. 调用服务初始化的真正执行者 ResolverRegistrationresolve 方法进行初始化服务对象。
  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 中,默认使用的 ResolverScopeResolverScopeGraph 类型,当然我们也可以在注册时,通过调用注册项的 scope 方法进行切换。

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

Resolving 协议

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

当我们使用静态方法时,对应的 Resolver 对象是一个全局变量 rootmain,两者其实是同一个 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。当已有类型满足不了需求时,我们可以对 Resolverregister 方法进行重载,并自定义 ResolverRegistration 子类。

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

总结

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

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

参考

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