在UIView.animateWithDuration(...)的闭包中是否需要使用[unowned self]?

54
    UIView.animateWithDuration(1,
        animations: { [unowned self] in
            self.box.center = self.boxTopRightPosition
        },
        completion: { [unowned self] completed in
            self.box.hidden = true
    })

需要避免内存泄漏吗?


这里的答案是误导性的。请参见此处 - mfaani
7个回答

72

不需要在这种情况下使用。 animationscompletion都没有被self保留,因此不存在强引用循环的风险。


1
有没有办法在这里创建像“animations”或“completion”这样的闭包? - WildCat
1
@Plot UIKit是用Objective-C编写的,并且在Swift中进行了插值。Objective-C没有@noescape功能。 - Kirsteins
7
有没有记录显示该块不被self所保留? - Tieme
3
如果 self 引用了 UIView 的强引用,那么闭包不是会被 self 间接地保留吗? - Bradley Thomas
1
@Tieme 没有文档记录。但请参见此处。确保您完全理解问题,然后阅读答案。它应该能很好地解释。 - mfaani
显示剩余7条评论

55

好的,“必要”并不等同于“推荐”。如果您的问题是是否必要,那么@Kirsteins的回答就足够了,但请想象一下这样一种情况:在完成某些工作后,您希望在视图控制器中动画显示某些内容,但您的视图控制器已被释放(因为它不再位于视图层次结构中或任何其他原因)。在这种情况下,如果您不使用[weak self],则您的视图控制器将在完成动画之前无法释放,因为您在动画块中保留了它,但将其保留到动画一个不再显示的视图中是否有意义呢?

因此,简而言之,在使用UIKit动画时,您需要使用一个weak引用来引用自身,但是,如果视图被释放,则无需保留视图,因为没有视图的动画是没有意义的,因此使用weak是一个好选择。


2
很好,也正确。不明白为什么这个答案不在最上面 :) - Nikolai Ischuk
4
没问题。简而言之,在调度队列/动画块中使用[weak self][unowned self]只是为了控制流程,而不是解决内存问题。最终它们会执行/完成并释放对象(取决于块所需的时间,可以是2秒、10秒或200秒)。更多相关信息,请参见如何使用dispatchQueues创建引用循环? - mfaani
1
完全同意你的观点,我曾经反复地对我的按钮进行动画处理,这导致了内存泄漏。但是我已经通过弱引用来解决了这个问题。 - Gürhan KODALAK
如果您尝试在没有使用weak self的情况下释放ViewController,那么这是错误的。动画完成后,将立即调用success = false并且VC将立即被释放。 - Xys

42

不需要。如Kirsteins所说:

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

但是lhmgrassi说:

只要它释放了,析构函数就会被调用,完成块将永远不会被执行。

我认为这并不正确。完成块总是会被调用。如果使用了强self,您的对象在完成块执行之前不会被释放。

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

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

我制作了一个示例来说明这一点。

[gif illustrating the issue

完整的源代码可以在 Github上找到


是的,由于你捕获了self,所以会导致“未拥有的self”崩溃。只使用self就可以安全地完成,因为完成块不会被self保留。 - José
是的,完全正确。 - Tieme
如果您不需要完成块保留内存 - 您可能正在执行无限动画循环或非常长的动画,而您不希望它继续保持内存,请使用[weak self] - CyberMew
你不需要使用 weak self,它是一个全局函数,不会保留 self - José
1
哦,那是很好的示例/动画片。谢谢! - Stotch

7

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

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

}

一旦它被销毁,析构函数将会被调用,并且完成操作将永远不会执行。

1
我认为完成处理程序将始终被调用,请查看我的答案。 - Tieme

6
恰恰相反。你希望self继续存在足够长的时间,以便调用完成块。因此,通过逃逸完成处理程序强制保留self是一件好事。
通常导致人们使用weak self的担忧是保留回路。但这不是那样的情况。保留回路是指self保留了闭包,闭包又保留了self,导致泄漏,因为现在self永远不能释放。但这根本不是那种情况。闭包,因此self被保留,但不是由self保留!因此,仍然有一些保留正在进行,但这是一个好事,而不是坏事。

3
在动画、GCD或完成处理程序中不需要使用weak/unowned,因为它们捕获的外部对象引用仅会被保留有限的时间,这意味着它肯定会在某个时间点执行。此后,它将被释放,因此不存在导致内存泄漏的引用循环。
正如之前的答案所建议的那样,

如果动画和完成处理程序没有被self保留,那么谁会保留它们?

我没有找到任何文件证据,但我相信它们由self自身保留,但只保留有限的时间。此后,完成处理程序执行并释放self,从而导致self的解除分配。
在保留周期场景中,闭包被self保留,而self被闭包保留了无限长的时间,这被认为是引用循环和内存泄漏。

1

这个主题上有很多错误的答案。对于动画,weak self 是不需要的。

一旦在你的 ViewController 上调用 dismiss,动画完成时将会被调用(成功为 false),即使有一个强引用的 self,你的 ViewController 也将被释放。

使用以下测试:

class TestVC: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .blue

        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.view.alpha = 1
            UIView.animate({
                self.view.alpha = 0
            }, duration: 40) { success in
                print("finished: \(success)")
                self.view.backgroundColor = .green
            }
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.dismiss(animated: true)
        }
    }

    deinit {
        print("deinit")
    }
}

(请注意40秒的动画持续时间) 在6秒后,您将获得以下日志:

finished: false
deinit

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接