源码解读——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
18public 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
3public 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
48public 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 | public class ResolverScope: ResolverScopeType { |
从 resolve
的实现来看,我们也能够确认
ResolverScope
是一个抽象类。除此之外,它还定义了几个静态变量。这几个静态变量的类型都是
ResolverScope
的子类,而且类型有所不同。
下面,我们来看看它的子类。
ResolverScopeCache
ResolverScopeCache
的定义如下,其内部维护了一个缓存哈希表
cacahedServices
。另外,它定义了一套自己的服务解析策略,从
resolve
方法可以看出大致的策略如下:
- 根据注册项的
cacheKey
查找服务对象,如果查到,则直接返回对应的服务对象;否则,进入步骤 2 - 调用服务初始化的真正执行者
ResolverRegistration
的resolve
方法进行初始化服务对象。 - 如果初始化完成,则加入缓存哈希表进行缓存。
- 最后,返回服务对象。
此外,ResolverScopeCache
还提供一个 reset
方法,可以清空缓存哈希表。
1 | /// Cached services exist for lifetime of the app or until their cache is reset. |
ResolverScopeGraph
ResolverScopeGraph
的定义如下所示,它也持有一个缓存哈希表,其服务解析策略与
ResolverScopeCache
大同小异,区别在于:它在一次解析周期中对于相同的服务只解析一次,当一次解析周期完毕后,自动清空缓存哈希表。
1 | /// Graph services are initialized once and only once during a given resolution cycle. This is the default scope. |
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
9public 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 非常适合作为重构的一套辅助工具,希望后面能够在实际开发中使用一下,从而能够在实践中提高一下自己的架构设计能力。