【问题标题】:Weird retain cycle in SwiftSwift 中奇怪的保留周期
【发布时间】:2018-11-15 14:57:49
【问题描述】:

所以我写了一些代码来更好地在保留周期下。

class AnotherViewController : UIViewController{

var closure : ((() -> Int) -> ())!


override func viewDidLoad() {
    super.viewDidLoad()

    closure = self.someFunctionWithNonescapingClosure

}

func someFunctionWithNonescapingClosure(closure: () -> Int) {
    //        closure()
}

}

显然,将 viewController 中的函数之一分配给属性闭包,会导致保留周期问题。

但我不知道怎么做?

Self 强烈引用 Closure 但是,在 viewController tp Closure 中分配一个函数,是否会强烈引用 self 到闭包?

谢谢

编辑 ------

显然,如果您通过创建 AnotherViewController 在 Playground 中尝试此操作,对其进行初始化并将其分配给一个变量,然后将变量设置为 nil,它将成功地 deinit the AnotherViewController,但如果您在应用程序中尝试, AnotherViewController 不会被取消初始化。

你可以尝试给AnotherViewController添加一个按钮并关闭它,为了方便,按钮代码是这样的

 private func addAButton(){
    let button = UIButton()
    let buttonBounds = CGRect(x: 0, y: 0, width: 200, height: 200)
    let buttonCenter = view.center
    button.bounds = buttonBounds
    button.center = buttonCenter

    view.addSubview(button)

    button.backgroundColor = .red

    button.addTarget(self, action: #selector(goBack), for: .touchUpInside)
}

@objc func goBack(){
    dismiss(animated: true, completion: nil)
}

【问题讨论】:

  • self对闭包有强引用,闭包内部也持有强self,所以退出函数后不会释放自己,会产生retain循环
  • @Tj3n 代码的哪一部分闭包保留了自我?这是我不明白的部分。
  • 我在操场上运行了你的代码,如果你将它的实例值设置为 nil,它会显示 AnotherViewController 的 deinit 被调用
  • @avinashpandey 感谢您的帮助.. 但是您可以在 singleViewController 中尝试。添加按钮以关闭 viewController 后,不会调用 deinit
  • @progammingBeignner 能否请您添加带有按钮的代码,它会更有帮助。

标签: ios swift


【解决方案1】:

您的closure 被分配了一个instance 方法,该方法隐式捕获self,因此循环。

试试以下应用:

import Cocoa

class Cycle
{
  var closure : ((() -> Int) -> ())!

  init()
  {
     closure = self.someFunctionWithNonescapingClosure
  }

  func someFunctionWithNonescapingClosure(closure: () -> Int)
  {
     print("Hello") // breakpoint on this line
  }
}

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate
{
  @IBOutlet weak var window: NSWindow!

  func applicationDidFinishLaunching(_ aNotification: Notification)
  {
     let aCycle = Cycle()
     aCycle.closure({ 42 })
  }
}

print 行添加断点并运行应用程序。

应用程序将在 someFunctionWithNonescapingClosure() 内停止,由 aCycle.closure({ 42 }) 调用。

查看变量显示,有一个self。这是因为每个实例方法都有一个 隐式 self 参数。

在您的代码中,self 来自哪里?

当行:

closure = self.someFunctionWithNonescapingClosure

被执行 Swift 捕获 self 的当前值作为隐式参数传递给 someFunctionWithNonescapingClosure(),它必须这样做,因为您正在从 instance 方法创建闭包。

这样你就有了循环,分配给closure 的闭包包含对self 的引用。

要查看此注释,请注意调试器停止时self 的值,然后在堆栈跟踪中选择applicationDidFinishLaunching 的条目并查看其aCycle 变量 - 它与self 的值相同someFunctionWithNonescapingClosure - 这是你的周期。

在堆栈跟踪中,您还将看到“部分应用”之类的条目 - 这是提供的参数 ({ 42 }) 和隐式捕获的 self 被收集并传递给 someFunctionWithNonescapingClosure() 的地方。

如果您将代码更改为:

  init()
  {
     closure = Cycle.someFunctionWithNonescapingClosure
  }

  static func someFunctionWithNonescapingClosure(closure: () -> Int)
  {
     print("Hello") // breakpoint on this line
  }

也就是说,将 someFunctionWithNonescapingClosure 设为类 (static) 方法,而不是在一个实例上,然后当前类实例不会在闭包中捕获,您将不会得到循环。

HTH

【讨论】:

    猜你喜欢
    • 2018-05-13
    • 1970-01-01
    • 2019-11-28
    • 2016-05-03
    • 2015-10-04
    • 2014-08-11
    • 2012-09-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多