在swift中,我可以使用实例方法作为闭包,例如,将方法分配给回调
self.someView.someCallback = self.doSomething
那么,self
在self.doSomething
中强烈引用了吗?上面的行是否创建了一个参考循环?
根据您的代码段,有两种可能的方案:
doSomething
是self
的实例方法,那么,是的,该行建立了一个强引用。请记住,Closures are Reference Types。您可以轻松确认这一点,并且很容易通过经验确认。考虑:
class ViewController: UIViewController {
var foo: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
foo = bar
foo?()
}
func bar() { ... }
}
如果我呈现和关闭此视图控制器三次,然后使用Xcode的“调试内存图”,我将看到这三个实例仍在内存中停留(在左侧),如果我选择一个,它将向我显示强引用在中心面板中直观地循环:
因为我使用了“Malloc堆栈”功能,所以在右侧面板上我可以准确地看到延迟强引用的位置,即在viewDidLoad
中设置闭包。doSomething
不是一个函数,而是一个闭包,那么该行本身并不会建立一个强引用,而是它成为一个问题,即闭包本身是否指的是self
,如果是的话,是否有[weak self]
或[unowned self]
捕获列表。有关更多信息,请参阅Strong Reference Cycles for Closures。为了有一个保留周期,你需要在每个方向上有一个强有力的参考,即:
对象A强烈引用对象B.
对象B强烈引用对象A.
假设您共享的代码中的self
是一个视图控制器,并假设someView
是对视图的强引用,我们可以这样说:
对象A(视图控制器)强烈引用对象B(某些视图)
现在,如果对象B(某些视图)具有强烈的参考回到视图控制器,您将有一个保留周期。
假设doSomething
是ViewController中的一个方法,而不是一个闭包,那么你将有一个保留周期
检查这个问题的简单方法是在Some View和View Controller中实现deinit
,如下所示:
class SecondViewController: UIViewController {
var someView: CustomView?
override func viewDidLoad() {
super.viewDidLoad()
someView = CustomView(frame: view.frame)
someView?.someCallback = doSomething
}
func doSomething() {
}
deinit {
print(#function)
}
}
final class CustomView: UIView {
var someCallback: (() -> Void)?
deinit {
print(#function)
}
}
您将看到print
上的deinit
s永远不会在控制台中打印出来。但是改变你将someCallback
分配给的方式:
someView?.someCallback = { [weak self] in
self?.doSomething()
}
将导致deinit
运行,从而打破保留周期
编辑:
甚至,作为替代方案:
weak var weakSelf = self
someView?.someCallback = weakSelf?.doSomething
(即使这是使用弱引用,因为在执行someCallback
的赋值时评估此表达式,而不是在执行它时,这仍将成为strong
引用) - 谢谢@Rob
在Swift中,声明一个闭包类型变量,并希望为它分配一个func,防止出现保留问题,只需按照以下步骤操作,一整天都在搜索答案,渴望分享:
self.someView.someCallback = {
[unowned self] in self.doSomething()
}