【发布时间】:2020-01-11 02:20:20
【问题描述】:
注意:这个问题与this one基本相同,但针对Swift 4或5。
假设我有一个捕获闭包的类:
class CallbackHolder {
typealias Callback = (String) -> Void
var callback: Callback
init(_ callback: @escaping Callback) {
self.callback = callback
}
func useCallback() {
self.callback("Hi!")
}
}
这个类只是在一个变量中保存一个回调,并且有一个使用该回调的函数。
现在,假设我有一个拥有这样一个回调持有者的客户端类。此类希望回调持有者调用其方法之一作为回调:
class Client {
func callback(string: String) {
print(string)
}
lazy var callbackOwner = CallbackOwner(callback: callback)
deinit {
print("deinit")
}
}
这个类有一个回调,一个调用该回调的回调所有者,以及一个打印一些东西的 deinit,以便我们知道我们是否有一个保留循环(没有 deinit = 保留循环)。
我们可以使用以下测试函数来测试我们的设置:
func test() {
Client().callbackOwner.useCallback()
}
我们希望测试函数同时打印Hi! 和deinit,以便我们知道回调有效,并且客户端不会受到保留周期的影响。
上面的Client实现其实确实有一个retain循环——把callback方法传给回调的owner会导致owner强retain client,造成循环。
当然,我们可以通过替换来修复循环
lazy var callbackOwner = CallbackOwner(callback: callback)
与
lazy var callbackOwner = CallbackOwner(callback: { [weak self] in
self?.callback($0)
})
这可行,但是:
- 很乏味(比较我们现在需要的代码量)
- 这很危险(我的 CallbackOwner 类的每个新客户端都必须记住这样做,即使原来的方式是完全有效的语法,否则他们会得到一个保留循环)
所以我正在寻找更好的方法。我想在 CallbackOwner 上弱捕获回调,而不是在客户端。这样,新客户就不必意识到保留周期的危险,他们可以以最直观的方式使用我的 CallbackOwner。
所以我尝试将CallbackOwner的回调属性改为
weak var callback: Callback?
当然,Swift 只允许我们弱捕获类类型,而不是闭包或方法。
在写this 答案的时候,似乎没有办法实现我正在寻找的东西,但那是 4 年前的事了。在最近的 Swift 版本中是否有任何进展可以让我将方法传递给闭包捕获对象而不会导致保留循环?
【问题讨论】:
-
前面的答案还是正确的。什么都没有改变。