简体   繁体   中英

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. 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.

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.

I have to remove the observer in deinit to solve this issue. 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:

'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? 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):

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 .

(I generally wouldn't put any KVO code in viewDidLoad because it has no partner callback from UIKit )

PS: you're also missing the call to super.viewDidAppear()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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