我的封口保留了下来。它导致捕获内部的所有其他对象。我可以使用弱引用传递此类对象,但是它不能解决保留周期的问题。在没有保留周期的情况下使用闭包进行递归的正确方法是什么?
class Foo {
var s = "Bar"
deinit {
print("deinit") // Won't be executed!
}
}
class TestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo() // Weak works, but not the right solution.
var closure: () -> Void = { return }
closure = {
print(foo.s)
if true {
return
} else {
closure()
}
}
}
}
答案 0 :(得分:3)
您有一个不寻常的设置,其中,关闭保留了它自己。请注意,Swift不允许您创建对闭包的弱引用。
要中断保留周期,请在递归的基本情况下将closure
设置为{ }
。这是一个测试macOS命令行程序:
func test() {
var closure: ((Int) -> ()) = { _ in }
closure = { i in
if i < 10 {
closure(i + 1)
} else {
// Comment out this line for unbounded memory consumption.
closure = { _ in }
}
}
closure(0)
}
while true {
test()
}
如果运行此命令,其内存消耗将保持不变。
如果在重设closure
的基本情况下注释掉该行,则其内存消耗将无限增长。
答案 1 :(得分:0)
您的closure
持有foo
个实例引用。
foo
一经发布,就会被释放。
closure
在自称。如果我们在closure
内传递弱self
,那应该没问题。或通过重置closure
下面的代码应该可以正常工作。
closure
或在var closure: () -> Void = { return }
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo()
closure = { [weak self] in
print(foo.s)
if true {
return
} else {
self?.closure()
}
}
}
内初始化foo
closure
答案 2 :(得分:0)
把你的闭包变成a nested function:
class Foo {
var s = "Bar"
deinit {
print("deinit")
}
}
class TestVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo()
func nestedFunction() {
print(foo.s)
if true {
return
} else {
nestedFunction()
}
}
nestedFunction()
}
}
在 Swift 中,嵌套函数可以同步(递归函数)或异步(通常用于异步迭代)引用自身,无需任何引用循环,并且可以像闭包一样捕获变量。您甚至可以拥有相互递归的嵌套函数。
您可以改为在完成后将包含闭包的变量重置为虚拟闭包,我并不是说这不起作用,但这很容易出错,尤其是当闭包异步调用自身时:在这种情况下,重置也必须异步完成。最好静态地确保没有引用循环,这在 Swift 中的大多数其他地方都可以做到。
(由于 gcc 在 C 语言中的实现引入了安全漏洞,因为试图将闭包引用压缩到 C 函数指针(即代码地址)中,因此这个概念曾经有一个不好的说唱,但 Swift 嵌套函数与此无关)