在swift中,我可以使用实例方法作为闭包,例如,将方法分配给回调
self.someView.someCallback = self.doSomething
那么self
强烈引用了self.doSomething
吗?上面的行是否创建了一个参考循环?
答案 0 :(得分:2)
根据您的代码段,有两种可能的方案:
如果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。
答案 1 :(得分:1)
为了有一个保留周期,你需要在每个方向都有一个强引用,即:
对象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
永远不会在控制台中打印出来。但是,请更改someCallback
指定的方式:
someView?.someCallback = { [weak self] in
self?.doSomething()
}
将导致deinit
运行,从而打破保留周期
修改强>
或者甚至,作为替代方案:
weak var weakSelf = self
someView?.someCallback = weakSelf?.doSomething
击> <击> 撞击>
(即使这是使用弱引用,因为在执行someCallback
的赋值时评估此表达式,而不是在执行它时,它仍将变为{{ 1}}参考) - 谢谢@Rob
答案 2 :(得分:0)
在Swift中,声明一个闭包类型变量,并希望为其分配功能,以防止出现保留问题, 请按照以下步骤操作,全天搜索答案,并希望与他人分享:
self.someView.someCallback = {
[unowned self] in self.doSomething()
}