简体   繁体   English

闭包递归和保留循环

[英]Closure recursion and retain cycles

My closure retains itself.我的关闭保留了它自己。 It causes capturing all other objects inside.它会导致捕获内部的所有其他对象。 I can pass such objects using weak reference, but it doesn't solve the problem of retain cycle.我可以使用弱引用传递这样的对象,但它并不能解决保留循环的问题。 What's the right way to do recursion with closures without retain cycles?在没有保留周期的情况下使用闭包进行递归的正确方法是什么?

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()
      }
    }
  }

}

You have an unusual setup where your closure retains itself.你有一个不寻常的设置,你的闭包保留了它自己。 Note that Swift doesn't allow you to create a weak reference to a closure.请注意,Swift 不允许您创建对闭包的弱引用。

To break the retain cycle, set closure to { } in the base case of the recursion.要中断保留循环,请在递归的基本情况下将closure设置为{ } Here's a test macOS command-line program:这是一个测试 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()
}

If you run this, its memory consumption is flat.如果你运行它,它的内存消耗是平的。

If you comment out the line in the base case that resets closure , its memory consumption grows without bound.如果您注释掉重置closure的基本情况中的行,则其内存消耗会无限制地增长。

Your closure is holding foo instance reference.您的closure持有foo实例引用。 foo will be released as soon as the closure is released. foo将在closure释放后立即释放。

closure is calling itself. closure在呼唤自己。 If we pass weak self inside closure then that should be fine.如果我们在closure传递弱self ,那应该没问题。 OR by resetting closure或通过重置closure

below code should work fine.下面的代码应该可以正常工作。

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()
        }
    }
}

OR initialize foo inside closure或在closure内初始化foo

override func viewDidLoad() {
    super.viewDidLoad()

    var closure: () -> Void = { return }
    closure = { [weak self] in
        let foo = Foo()
        print(foo.s)
        if true {
            return
        } else {
            self?.closure()
        }
    }
}

Turn your closure into 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()
  }

}

In Swift nested functions can refer to themselves synchronously (recursive functions) or asynchronously (typically for asynchronous iteration), can do so without any reference cycle, and can capture variables just as well as closures do.在 Swift 中,嵌套函数可以同步(递归函数)或异步(通常用于异步迭代)引用自己,可以在没有任何引用循环的情况下这样做,并且可以像闭包一样捕获变量。 You can even have mutually recursive nested functions.你甚至可以有相互递归的嵌套函数。

You could instead reset the closure-containing variable to a dummy closure once done, I am not saying this does not work, but this is very error-prone, especially when the closure calls itself asynchronously: the reset has to be done asynchronously as well in that case.可以改为在完成后将包含闭包的变量重置为虚拟闭包,我并不是说这不起作用,但这很容易出错,尤其是当闭包异步调用自身时:重置也必须异步完成在这种情况下。 Better have the lack of a reference cycle be ensured statically, as can be done most everywhere else in Swift.最好静态地确保没有引用循环,这在 Swift 中的大多数其他地方都可以做到。

(The concept used to have a bad rap due to an implementation in the C language by gcc that introduced security holes as a result of attempting to squeeze a closure reference into a C function pointer ie a code address, but Swift nested functions have nothing to do with that) (由于 gcc 在 C 语言中的实现,由于试图将闭包引用压缩到 C 函数指针(即代码地址)中而引入了安全漏洞,因此这个概念曾经有一个不好的说唱,但 Swift 嵌套函数没有什么可这样做)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM