简体   繁体   English

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

[英]Is it necessary to use [unowned self] in closures of UIView.animateWithDuration(...)?

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

Is it necessary to avoid memory leak?是否有必要避免 memory 泄漏?

No, it is not needed in this case. 不,在这种情况下不需要。 animations and completion are not retained by self so there is no risk of strong retain cycle. animationscompletion不被保留self ,所以没有强烈的保留周期的风险。

Well, "necessary" isn't the same as "recommended". 好吧,“必需”与“推荐”不同。 If your question is if it's necessary then @Kirsteins' response is fine, however imagine the situation where you want to animate something in your view controller after some work, but your view controller has been released (because it is not in the view hierarchy anymore or any other reason). 如果您的问题是是否有必要,那么@Kirsteins的响应很好,但是请设想一下您想在完成一些工作后在视图控制器中对某些东西进行动画处理,但视图控制器已被释放的情况(因为它不再在视图层次结构中了)或任何其他原因)。 In this case, if you don't use [weak self] , your view controller won't get released until finishing the animation because you are retaining it in the animation block, but does it make sense to keep it retained until animating something which is not in the view anymore? 在这种情况下,如果您不使用[weak self] ,则在完成动画之前不会释放视图控制器,因为您将其保留在动画块中,但是将其保留直到动画化某些东西才有意义不在视野中了吗?

So, in few words, you don need to use a weak reference to self when animating UIKit, however, you don't need to keep your view retained if it's released, because an animation with no view doesn't make sense, so using weak is a good option. 因此,简而言之,在对UIKit进行动画处理时, 不需要使用对自身的weak引用,但是,如果发布了视图,则无需保留视图,因为没有视图的动画是没有意义的,因此使用weak是一个不错的选择。

No it's not needed. 不,不需要。 As Kirsteins says: 正如柯斯坦斯所说:

No, it is not needed in this case. 不,在这种情况下不需要。 animations and completion are not retained by self so there is no risk of strong retain cycle. 动画和完成不是自己保留的,因此没有强大的保留周期的风险。

But lhmgrassi says: 但是lhmgrassi说:

As soon as it be deallocated, the deinitializer will be called and the completion will never be executed. 一旦解除分配,将调用反初始化器,并且永远不会执行完成。

I don't think this is true. 我不认为这是真的。 The completion block will always be called. 完成模块将始终被调用。 And if you use a strong self your object won't be deallocated until the completion block is executed. 而且,如果您使用强大的自我,则在执行完成块之前,不会释放对象。

However, if you use a [weak self] , your object is not (temporary) retained by the completion block and might be deallocated before the completion block is fired. 但是,如果使用[weak self] ,则完成块不会(临时)保留您的对象,并且可能在激发完成块之前将其释放。 The completion block is still fired but self is already nil . 完成块仍被触发,但self已为nil

If you use a [unowned self] in your completion handler, you object might also be deallocated before the completion handler is called, which could result in a crash! 如果在完成处理程序中使用[unowned self] ,则在完成处理程序被调用之前,对象也可能会被释放,这可能导致崩溃!

I've made an example illustrating this. 我已经举例说明了这一点。

[gif说明问题

Full source can be found on Github 完整的源代码可以在Github找到

@Plabo, as @Kirsteins said, animations and completion are not retained by self, so even if you start a animation and for any reason your view controller has been released, it will deallocated instantaneously. 正如@Kirsteins所说,@ Plabo不会自行保留动画和完成,因此,即使您开始动画并且由于某种原因释放了视图控制器,它也会立即释放。 So, you don't need captured 'self'. 因此,您不需要捕获的“自我”。 Consider the silly example bellow: 考虑下面这个愚蠢的例子:

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

}

As soon as it be deallocated, the deinitializer will be called and the completion will never be executed. 一旦解除分配,将调用反初始化器,并且永远不会执行完成。

Just the opposite. 恰好相反。 You want self to continue to exist long enough for the completion block to be called. 希望 self继续存在足够长的时间,以便调用完成块。 Therefore having self be strong and retained through the escaping completion handler is a good thing. 因此具有self坚强,通过退避完成处理保留是一件好事

The worry that usually leads people to use weak self is a retain cycle . 通常导致人们使用weak self的烦恼是一个保留周期 But this isn't that. 但这不是那样。 A retain cycle is when self retains the closure which retains self , causing a leak because now self can never be released. 一个保留周期时, self保留保留了封闭self ,造成泄漏,因为现在self可以永远不会被释放。 But this is not that situation at all. 但这根本不是那种情况。 the closure, and therefore self , is being retained, but not by self ! 封闭,因此self ,被保留,而不是self保留! So there is some retaining going on, temporarily, but it is good , not bad. 因此,暂时会有一些保留,但这很好 ,还不错。

It is not needed to use weak/unowned in Animations, GCD or completion handler because : 不需要在Animations,GCD或完成处理程序中使用弱/无用的因为:

The external object reference captured by them will only be kept for fixed time , which means it's definitive that it's going to be executed at one point of time. 它们捕获的外部对象引用将仅保留固定的时间 ,这意味着它将在某个时间点执行。 After this, it will be released so there is no chance of reference cycle which leads to memory leak. 此后,它将被释放,因此不会有导致内存泄漏的参考周期机会。

As previous answers suggests, 如先前的答案所示,

If animations and completion are not retained by self, then who retains them ? 如果动画完成不是自己保留的,那么谁保留它们?

I didn't find any documentary evidence of this but what I believe is that they are retained by self itself but for a fixed amount of time . 我没有找到任何书面证据,但我相信它们是自己保留的,但是固定的时间里 After this, the completion executes and release the self, which result the de-allocation of self. 此后,完成执行并释放自我,这导致自我的取消分配。

In the retain cycle scenario, the closure is retained by self and self by closures for indefinite time, which is considered as reference cycle and memory leak. 在保留周期方案中,闭包将由self和self由闭包保留不确定的时间,这被视为参考周期和内存泄漏。

Many wrong answers on this subject.关于这个问题的许多错误答案。 weak self is never needed for animations.动画永远不需要weak self

As soon as you call dismiss on your ViewController, the animation completion will be called (with success at false), and your ViewController will be deallocated, even with a strong self reference.一旦您在 ViewController 上调用dismiss ,将调用 animation 完成(成功为 false),并且您的 ViewController 将被释放,即使有很强的自引用也是如此。

With this test:通过这个测试:

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

In the logs, you will get:在日志中,您将获得:

finished: false
deinit

After 6 seconds 6 秒后

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

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