关闭递归并保留周期

时间:2018-11-14 19:31:11

标签: ios swift

我的封口保留了下来。它导致捕获内部的所有其他对象。我可以使用弱引用传递此类对象,但是它不能解决保留周期的问题。在没有保留周期的情况下使用闭包进行递归的正确方法是什么?

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

}

3 个答案:

答案 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 嵌套函数与此无关)