【问题标题】:Is it necessary to use [unowned self] in closures of UIView.animateWithDuration(...)?是否有必要在 UIView.animateWithDuration(...) 的闭包中使用 [unowned self]?
【发布时间】:2015-01-17 03:19:15
【问题描述】:
    UIView.animateWithDuration(1,
        animations: { [unowned self] in
            self.box.center = self.boxTopRightPosition
        },
        completion: { [unowned self] completed in
            self.box.hidden = true
    })

是否需要避免内存泄漏?

【问题讨论】:

  • 这里的答案具有误导性。见here

标签: ios swift memory-management uiviewanimation retain-cycle


【解决方案1】:

不,在这种情况下不需要。 animationscompletion 不会被 self 保留,因此不存在强保留周期的风险。

【讨论】:

  • 有没有办法在这里创建像animationscompletion 这样的闭包?
  • @Plot UIKit 是用 Objective-C 编写的,它是用 Swift 插值的。 Objective-C 没有@noescape 特性。
  • 是否在某处记录了该块因此不被自己保留?
  • 如果 self 保留了对 UIView 的强引用,那么闭包不就是 self 间接保留的吗?
  • @Tieme 未记录。但请参阅here。确保您完全理解问题,然后阅读答案。应该很好解释
【解决方案2】:

嗯,“必要”与“推荐”不同。如果您的问题是是否有必要,那么@Kirsteins 的回答很好,但是想象一下您想在某些工作后在视图控制器中为某些东西设置动画但您的视图控制器已被释放的情况(因为它不再在视图层次结构中或任何其他原因)。在这种情况下,如果您不使用[weak self],您的视图控制器将在完成动画之前不会被释放,因为您将它保留在动画块中,但是保留它是否有意义,直到制作动画不在视图中了?

因此,简而言之,您在为 UIKit 制作动画时需要使用 weak 对 self 的引用,但是,如果视图已发布,则无需保留它,因为没有视图的动画没有意义,所以使用weak 是一个不错的选择。

【讨论】:

  • 不错且正确。不明白为什么这个答案不是最重要的:)
  • 正确。长话短说,在 dispatchQueues/animation 块中使用 [weak self][unowned self] 只是控制流的手段。这不是内存问题。 最终他们将执行/完成并释放对象(取决于块,它可能在 2 秒或 10 秒或 200 秒内)。有关更多信息,请参阅How can I create a reference cycle using dispatchQueues?
  • 完全同意你的观点,我反复为我的按钮设置动画,这会导致内存泄漏。我已经用弱参考解决了。
【解决方案3】:

不,不需要。正如Kirsteins 所说:

不,在这种情况下不需要。动画和完成不会被 self 保留,因此不存在强保留周期的风险。

但是lhmgrassi 说:

一旦它被释放,deinitializer 将被调用并且完成永远不会被执行。

我认为这不是真的。将始终调用完成块。如果你使用一个强大的 self 你的对象在完成块执行之前不会被释放。

但是,如果您使用[weak self],则您的对象不会(临时)由完成块保留,并且可能在触发完成块之前被释放。完成块仍然被触发,但self 已经是nil

如果您在完成处理程序中使用[unowned self],您的对象也可能在完成处理程序被调用之前被释放,这可能导致崩溃!

我做了一个例子来说明这一点。

完整来源可以是found on Github

【讨论】:

  • 是的,它会导致unowned self 崩溃,因为您正在捕获自我。只使用 self 是安全的,因为 self 不会保留完成块。
  • 是的,完全正确。 ?
  • 或者使用[weak self],如果你不需要完成块来保存内存——你可能正在做一个无限的动画循环或一个很长的动画,你不希望它继续持有进入记忆。
  • 你不需要weak self,它是一个全局函数,不会保留self
  • 哦,这是一个很好的示例/动画剪辑。谢谢!
【解决方案4】:

@Plabo,正如@Kirsteins 所说,动画和完成不会由自己保留,因此即使您启动动画并且出于任何原因您的视图控制器已被释放,它也会立即释放。因此,您不需要捕获的“自我”。 考虑下面这个愚蠢的例子:

class ViewController: UIViewController {

    @IBOutlet weak var button : UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        print("viewDidLoad ViewController")
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        UIView.animate(withDuration: 20, animations: {
            self.button.frame = CGRect(x: 0, y: 300, width: 30, height: 30)
        }) { finished in
            self.button.frame = CGRect(x: 0, y: 100, width: 30, height: 30)
        }
    }

    deinit {
        print("deinit ViewController")
    }

}

一旦它被释放,deinitializer 将被调用并且完成永远不会被执行。

【讨论】:

  • 我认为完成处理程序会一直被调用,看我的回答
【解决方案5】:

恰恰相反。您希望 self 继续存在足够长的时间以调用完成块。因此,让self 强大并通过转义完成处理程序保留是一件好事

通常导致人们使用weak self 的担忧是保留循环。但这不是那个。保留周期是self 保留保留self 的闭包,导致泄漏,因为现在self 永远无法释放。但这根本不是那种情况。关闭,因此self,被保留,但不是self!所以暂时会有一些保留,但它,还不错。

【讨论】:

    【解决方案6】:

    动画、GCD 或完成处理程序中不需要使用弱/无主,因为:

    他们捕获的外部对象引用只会保留 固定时间,这意味着它肯定会在某个时间点执行。在此之后,它将被释放,因此不会有导致内存泄漏的引用循环。

    正如之前的答案所暗示的,

    如果animationscompletion不是自己保留的,那么谁保留呢?

    我没有找到任何文件证据证明这一点,但我相信它们会自行保留,但会保留一段固定的时间。之后,completion 执行并释放 self,从而导致 self 的释放。

    在retain cycle场景中,闭包被self和self被闭包无限期地保留,这被认为是引用循环和内存泄漏。

    【讨论】:

      猜你喜欢
      • 2015-07-02
      • 2014-08-10
      • 1970-01-01
      • 2016-12-08
      • 2015-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多