首页 > 解决方案 > 为什么在视图控制器被取消初始化并导致 SIGABRT 或 EXC_BAD_ACCESS 后观察者仍然活着?

问题描述

我有一个带有两个视图控制器的简单应用程序,第一个带有一个按钮来显示第二个,它从带有 AVPlayerViewController 和 AVPlayer 的 URL 播放视频。在第二个视图控制器的 viewDidAppear() 中,我初始化 AVPlayerViewController,配置 AVPlayer 并向 AVPlayer 添加观察者以检测视频何时开始播放。

如果我在视频开始之前手动关闭第二个视图控制器(它必须快速关闭,使用慢速互联网连接可以帮助做到这一点)我得到一个 EXC_BAD_ACCESS 因为它似乎调用了 observeValue() 但访问的变量此方法(此处为容器视图)不再存在。

我必须删除 deinit 中的观察者才能解决此问题。当我尝试在 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.'

有人知道为什么吗?

苹果文档还说:

接收此消息的对象和观察者都不会被保留。

为什么我需要删除 deinit 中的观察者?换句话说,在我的情况下,为什么观察者在 deinit 之后仍然活着?

这是第二个视图控制器的代码(第一个视图控制器只有一个 UIButton 用于 show segue 并嵌入在 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
        }
    }
}

标签: iosswiftuikitkey-value-observing

解决方案


如果不实际调试应用程序,很难说它为什么会崩溃。但我会在/和中配对addObserver/ 。removeObserverviewWillAppearviewDidDisappear initdealloc

(我一般不会放任何 KVO 代码,viewDidLoad因为它没有来自 的合作伙伴回调UIKit

PS:你也错过了电话super.viewDidAppear()


推荐阅读