Swinject 源码框架(二):循环依赖的解决
可能存在循环依赖,比如 Parent
强制有 Child
, Child
弱持有 Parent
。
具体实现如下。Parent
初始化时,必须传入 Child
,而 Child
初始化不必传入 Parent
。
protocol ParentProtocol: AnyObject { }
protocol ChildProtocol: AnyObject { }
class Parent: ParentProtocol {
let child: ChildProtocol?
init(child: ChildProtocol?) {
self.child = child
}
}
class Child: ChildProtocol {
weak var parent: ParentProtocol?
}
具体调用方式如下:
let container = Container()
container.register(ParentProtocol.self) { r in
let child = r.resolve(ChildProtocol.self)!
let parent = Parent(child: child)
return parent
}
container.register(ChildProtocol.self) { _ in Child() }
.initCompleted { r, c in
let child = c as! Child
child.parent = r.resolve(ParentProtocol.self)
}
let parent = container.resolve(ParentProtocol.self)
注意把 Child
的属性赋值放在了 initCompleted
里。
结合代码看时序

// 如果已经存在实例了,就直接返回。可以避免了无限循环
if let persistedInstance = entry.storage.instance(inGraph: currentObjectGraph), let persistedService = persistedInstance as? Service {
return persistedService
}
//生成一个新的实例
let resolvedInstance = invoker(entry.factory as! Factory)
// 最开始没有实例,可能在调用invoker后,可能已经有新的实例在entry 的 strage 里了,比如上面的 P1。
if let persistedInstance = entry.storage.instance(inGraph: currentObjectGraph), let persistedService = persistedInstance as? Service {
// An instance for the key might be added by the factory invocation.
return persistedService
}
//把生成的实例保存起来
entry.storage.setInstance(resolvedInstance as Any, inGraph: currentObjectGraph)
// 初始化完成以后,调用 initComplete
if let completed = entry.initCompleted as? (Resolver, Any) -> Void,
let resolvedInstance = resolvedInstance as? Service {
completed(self, resolvedInstance)
}
return resolvedInstance as? Service
依赖的类型
上面的例子中,Child
是 Parent
的初始化参数,Parent
是 Child
的属性。这种依赖称为 Initializer/Property Dependencies.
也可能 Child
和 Parent
分别是其依赖的属性,这种依赖称为 Initializer/Property Dependencies。
也可能 Child
和 Parent
分别是其依赖的初始化参数。这种依赖,使用 Swinject,暂时无法解决无限循环的问题。
循环依赖可能导致的问题
某一个factory方法可能会被执行两次。比如上面例子中,Parent 对应的 factory 方法被执行了两次。
可能导致一些副作用,比如更加耗时。
一种解决方案是把依赖都放进在initCompleted
闭包里,这也意味着不能使用 Initializer/Property Dependencies
container.register(ParentProtocol.self) { _ in Parent()
}.initCompleted { (r, p) in
let parent = p as! Parent
parent.child = r.resolve(ChildProtocol.self)!
}
container.register(ChildProtocol.self) { _ in Child()}
.initCompleted { r, c in
let child = c as! Child
child.parent = r.resolve(ParentProtocol.self)
}
源码中和循环依赖有关的变量
- resolutionDepth
每次调用resolve自增。如果有循环依赖,会调用多次。 - maxResolutionDepth
用来探测无限循环 - currentObjectGraph
标记某次生成实例的过程 - GraphStorage
在某次解决循环依赖过程中,生成的实例都存储在这里。
下起雨,也要勇敢前行