借鉴了SwiftUI中@Environment属性包装器的方案
属性包装器允许注入依赖项并减少实现端的代码混乱。不需要大的初始值设定项,并且仍然有可能覆盖测试的依赖项。属性包装器还可以明确注入哪些属性,这可以提高可读性。
用到的技术:静态下标、扩展和属性包装器
从调用开始,倒序介绍调用栈,分析源码
使用propertyWrappper创建了一个新的对象networkProvider,它是一个协议对象,所以只要是遵循NetworkProviding的实例都可以赋值给networkProvider,然后进一步注意propertyWrappper创建的key
struct DataController {
@Injected(\.networkProvider) var networkProvider: NetworkProviding
func performDataRequest() {
networkProvider.requestData()
}
func printNetworkProvider() {
print(networkProvider)
}
}
注意.\networkProvider 是keyPath
@propertyWrapper
struct Injected<T> {
private let keyPath: WritableKeyPath<InjectedValues, T>
var wrappedValue: T {
get { InjectedValues[keyPath] }
set { InjectedValues[keyPath] = newValue }
}
init(_ keyPath: WritableKeyPath<InjectedValues, T>) {
self.keyPath = keyPath
}
}
初始化方法传入的参数是keyPath,这个keyPath会到放wrappedValue中,进一步,开始访问InjectionValues
/// Provides access to injected dependencies.
struct InjectedValues {
/// This is only used as an accessor to the computed properties within extensions of `InjectedValues`.
private static var current = InjectedValues()
/// A static subscript for updating the `currentValue` of `InjectionKey` instances.
static subscript<K>(key: K.Type) -> K.Value where K : InjectionKey {
get { key.currentValue }
set { key.currentValue = newValue }
}
/// A static subscript accessor for updating and references dependencies directly.
static subscript<T>(_ keyPath: WritableKeyPath<InjectedValues, T>) -> T {
get { current[keyPath: keyPath] }
set { current[keyPath: keyPath] = newValue }
}
}
propertyWrapper中wrappedValue,InjectedValues[keyPath],
调用InjectedValues中static subscript<T>(_ keyPath: WritableKeyPath<InjectedValues, T>) -> T
,即第二个静态方法,
这个静态方法中,将会使用InjectedValues其内部的私有静态属性current,它是一个InjectedValues结构体实例;
进一步使用keyPath进行读取和设置,这个keyPath就是@Injected(\.networkProvider) var networkProvider: NetworkProviding
中对应的**.networkProvider,而这个networkProvider,是在InjectedValues扩展中定义的**
current[keyPath: keyPath] 执行时,会调用第一个静态方法static subscript<K>(key: K.Type) -> K.Value whereK : InjectionKey,因为keyPath是遵循
//InjectedValues中定义新的计算属性
extension InjectedValues {
var networkProvider: NetworkProviding {
get { Self[NetworkProviderKey.self] }
set { Self[NetworkProviderKey.self] = newValue }
}
//suscript是static静态方法,需要通过类型本身调用,Self代表他所出现范围的类型别名
//NetworkProviderKey.self]代表NetworkProviderKey类型的元类型(meta-type),就是类型的类型,因为后面要使用类型的静态(static)方法,不能是类型实例,所以需要传元类型
}
//InjectionKey的实现
private struct NetworkProviderKey: InjectionKey {
static var currentValue: NetworkProviding = NetworkProvider()
}
其中NetworkProviderKey是遵循InjectionKey的一个实现,内部实现了currentValue,注意currentValue的定义,遵循NetworkProviding,赋值是NetworkProvider(),它是遵循NetworkProviding协议的一个实例,currentValue起到的作用是针对NetworkProviderKey的默认值,也就是外部使用时,不设置NetworkProviderKey时,默认是NetworkProvider();
然后Self[NetworkProviderKey.self],会调用到InjectedValues中的static subscript<K>(key: K.Type) -> K.ValuewhereK : InjectionKey
dataController.networkProvider = NetworkProvider()
|
Injected init(_ keyPath: WritableKeyPath<InjectedValues, T>)
|
var wrappedValue: T
|
InjectedValues static subscript(_ keyPath: WritableKeyPath<InjectedValues, T>) -> T
|
var networkProvider: NetworkProviding
|
InjectedValues static subscript(key: K.Type) -> K.ValuewhereK : InjectionKey
|
current
在父类创建了包含@Injected的propertyWrapper属性,且父类没有销毁的情况下,子类和父类都可以维护属性的值,完成(1)依赖注入和替换 (2)值传递