我的应用出现随机崩溃(无法在我拥有的设备上重现),但异常:
由于未注册为观察者,因此无法从AVPlayerLayer 0xaddress中删除键路径“ readyForDisplay”的观察者Foundation.NSKeyValueObservation 0x地址。
当我取消分配包含AVPlayerLayer的UIView时会发生这种情况。
我的初始化:
private var playerLayer : AVPlayerLayer { return self.layer as! AVPlayerLayer }
init(withURL url : URL) {
...
self.asset = AVURLAsset(url: url)
self.playerItem = AVPlayerItem(asset: self.asset)
self.avPlayer = AVPlayer(playerItem: self.playerItem)
super.init(frame: .zero)
...
let avPlayerLayerIsReadyForDisplayObs = self.playerLayer.observe(\AVPlayerLayer.isReadyForDisplay, options: [.new]) { [weak self] (plLayer, change) in ... }
self.kvoPlayerObservers = [..., avPlayerLayerIsReadyForDisplayObs, ...]
...
}
我的引发异常的deinit:
deinit {
self.kvoPlayerObservers.forEach { $0.invalidate() }
...
NotificationCenter.default.removeObserver(self)
}
根据Crashlytics,它发生在不同iPhone上的iOS 11.4.1上。
导致deinit
的代码非常简单:
// Some UIViewController context.
self.viewWithAVLayer?.removeFromSuperview()
self.viewWithAVLayer = nil
我很想知道为什么会这样。
我见过this bug,但这似乎不是我的原因。
编辑1:
后代的其他信息。在iOS 10上,如果我不无效,则在deinit上会出现可重现的崩溃。在iOS 11上,它可以在没有失效的情况下正常工作(如果我不失效并让观察者与我的班级一起deinit
,崩溃是否会消失还没有检查)。
编辑2:
后代的其他信息:我还发现了这个可能与Swift相关的错误-SR-6795。
答案 0 :(得分:8)
之后
self.kvoPlayerObservers.forEach { $0.invalidate() }
添加
self.kvoPlayerObservers.removeAll()
我也不喜欢这一行:
self.kvoPlayerObservers = [..., avPlayerLayerIsReadyForDisplayObs, ...]
kvoPlayerObservers
应该是一个Set,接收观察者时,应一个接一个地插入它们。
答案 1 :(得分:0)
我已经接受了 matt 的回答,但是我想提供更多有关如何实际解决问题的信息。
我不会崩溃的deinit看起来像这样:
if let exception = tryBlock({ // tryBlock is Obj-C exception catcher.
self.kvoPlayerObservers.forEach { $0.invalidate() };
self.kvoPlayerObservers.removeAll()
}) {
remoteLoggingSolution.write(exception.description)
}
... // do other unrelated stuff
基本上,我尝试捕获Obj-C异常(如果发生),并尝试将其远程记录。
过去2周,我已经将此代码投入生产,从那时起,我既没有收到崩溃也没有收到异常日志,因此我认为 matt 的添加kvoPlayerObservers.removeAll()
的建议是正确的(至少对于我的特定情况而言。)