繁体   English   中英

闭包递归和保留循环

[英]Closure recursion and 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()
      }
    }
  }

}

你有一个不寻常的设置,你的闭包保留了它自己。 请注意,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的基本情况中的行,则其内存消耗会无限制地增长。

您的closure持有foo实例引用。 foo将在closure释放后立即释放。

closure在呼唤自己。 如果我们在closure传递弱self ,那应该没问题。 或通过重置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()
        }
    }
}

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

把你的闭包变成一个嵌套函数

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 嵌套函数没有什么可这样做)

暂无
暂无

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

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