ios - 为什么在视图控制器被取消初始化并导致 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
}
}
}
解决方案
如果不实际调试应用程序,很难说它为什么会崩溃。但我会在/或和中配对addObserver
/ 。removeObserver
viewWillAppear
viewDidDisappear
init
dealloc
(我一般不会放任何 KVO 代码,viewDidLoad
因为它没有来自 的合作伙伴回调UIKit
)
PS:你也错过了电话super.viewDidAppear()
推荐阅读
- nginx - 如何重定向到 https,除了模式
- python - 如何将一行和一列特征成对组合到keras中的特征矩阵?
- ansible - 如何使用 Ansible 将项目克隆到 GCE?
- mips - 右对齐 5 位 29-25 和零扩展。mips/组装
- spring - SLF4j 的 Websphere 8.5 部署问题
- javascript - 文件更改时刷新视图 - CsvToHtmlTable 库
- javascript - 打字稿 - 从当前日期计算月份和年份
- ios - Firebase 身份验证代码问题(Swift、iOS)
- python - num2date 不支持“自...以来的月份”
- android - Google Drive REST API:无法解析方法“getFiles()”