简体   繁体   English

为什么在取消初始化视图控制器并导致SIGABRT或EXC_BAD_ACCESS后观察者仍处于活动状态?

[英]Why observer stay alive after a view controller is deinitialized and causes SIGABRT or EXC_BAD_ACCESS?

I have a simple application with two view controllers, the first one with a button to show the second one which plays a video from an URL with an AVPlayerViewController and AVPlayer. 我有一个带有两个视图控制器的简单应用程序,第一个带有按钮,以显示第二个视图控制器,第二个使用AVPlayerViewController和AVPlayer从URL播放视频。 In the viewDidAppear() of the second view controller I initialize the AVPlayerViewController, configure the AVPlayer and add an observer to the AVPlayer to detect when the video start playing. 在第二个视图控制器的viewDidAppear()中,我初始化AVPlayerViewController,配置AVPlayer并将观察者添加到AVPlayer以检测视频何时开始播放。

If I dismiss manually the second view controller before the video has started (it has to be dismissed rapidly, using a slow Internet connection can help to do this) I get a EXC_BAD_ACCESS because it seems that observeValue() get called but the variable accessed in this method (here a container view) does not existing anymore. 如果我在视频开始播放之前手动关闭了第二个视图控制器(必须迅速将其关闭,则使用缓慢的Internet连接可以帮助完成此操作),我得到一个EXC_BAD_ACCESS,因为似乎调用了watchValue(),但是在此方法(此处为容器视图)不再存在。

I have to remove the observer in deinit to solve this issue. 我必须删除deinit中的观察者才能解决此问题。 When I try to remove it in other places like viewWillDisappear() sometimes the observer is not even added so I get a SIGABRT (strange because I add it in viewDidAppear()) when removing: 当我尝试在其他地方(例如viewWillDisappear())删除它时,有时甚至没有添加观察者,因此删除时我得到了SIGABRT(奇怪,因为我在viewDidAppear()中添加了它):

'Cannot remove an observer <App.SecondViewController 0x101805600> for the key path "status" from <AVPlayer 0x1c801a0b0> because it is not registered as an observer.'

Someone know why? 有人知道为什么吗?

And the Apple documentation also says: 苹果文档也说:

Neither the object receiving this message, nor observer, are retained. 既不保留接收此消息的对象,也不保留观察者。

Why exactly do I need to remove the observer in deinit? 为什么我需要在deinit中删除观察者? In other words, why the observer stays alive after deinit in my case? 换句话说,在我的情况下,为什么观察者在重新初始化后仍然存活?

Here is the code for the second view controller (the first one only have a UIButton for the show segue and is embedded in a NavigationController): 这是第二个视图控制器的代码(第一个视图控制器仅具有用于show segue的UIButton,并且嵌入在NavigationController中):

import UIKit
import AVKit

class SecondViewController: UIViewController {
    @IBOutlet weak var containerView: UIView!

    var player: AVPlayer!

    deinit {
        //player.removeObserver(self, forKeyPath: "status")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

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

        let url = URL(string: "http://clips.vorwaerts-gmbh.de/VfE_html5.mp4")

        // Create the player and player view controller.
        player = AVPlayer(url: url!)
        let playerViewController = AVPlayerViewController()
        playerViewController.player = player

        // Add observer to detect when the video start playing.
        player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)

        // Add the player controller in the container view.
        self.addChildViewController(playerViewController)
        self.containerView.addSubview(playerViewController.view)
        playerViewController.view.frame = containerView.bounds
        playerViewController.didMove(toParentViewController: self)

        player.play()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)

        player.removeObserver(self, forKeyPath: "status")
    }

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

        //player.removeObserver(self, forKeyPath: "status")
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        print("SndVC - Memory Warning")
    }

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if keyPath == "status" {
            // Perform a task on the container view.
            self.containerView.backgroundColor = UIColor.purple
        }
    }
}

It's difficult to say why it's crashing without actually debugging the app. 很难说为什么在不实际调试应用程序的情况下崩溃。 But I would pair addObserver / removeObserver either in viewWillAppear / viewDidDisappear or init and dealloc . 但是我会在viewWillAppear / viewDidDisappear initdealloc addObserver / removeObserver配对。

(I generally wouldn't put any KVO code in viewDidLoad because it has no partner callback from UIKit ) (我通常不会在viewDidLoad放置任何KVO代码,因为它没有来自UIKit伙伴回调)

PS: you're also missing the call to super.viewDidAppear() PS:您还缺少对super.viewDidAppear()的调用

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

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