fix: use sublayer instead of replacing NSView backing layer to prevent autorelease crash
- PlayerNSView: AVPlayerLayer as sublayer (not layer= replacement) NSView internally manages a sublayer array; replacing the backing layer directly causes dangling refs during autorelease pool drain - cleanup(): only pause + remove observers, no replaceCurrentItem CoreMedia internal state gets corrupted when item is replaced during view teardown; let it release naturally with deinit
This commit is contained in:
parent
aa19ab9799
commit
f14d47b5c8
@ -388,12 +388,13 @@ final class PlayerBridge: ObservableObject {
|
||||
|
||||
/// 主动清理所有观察者,在 app 退出前调用
|
||||
func cleanup() {
|
||||
player.pause()
|
||||
if let obs = timeObserver { player.removeTimeObserver(obs); timeObserver = nil }
|
||||
itemStatusObserver?.invalidate(); itemStatusObserver = nil
|
||||
playbackStatusObserver?.invalidate(); playbackStatusObserver = nil
|
||||
if let token = endObserverToken { NotificationCenter.default.removeObserver(token); endObserverToken = nil }
|
||||
player.pause()
|
||||
player.replaceCurrentItem(with: nil)
|
||||
// 注意:不调用 replaceCurrentItem(with: nil)
|
||||
// 让 playerItem 随 player 自然释放,避免 CoreMedia 内部状态不一致
|
||||
}
|
||||
|
||||
deinit {
|
||||
|
||||
@ -54,26 +54,29 @@ struct VideoPlayerRepresentable: NSViewRepresentable {
|
||||
}
|
||||
|
||||
class PlayerNSView: NSView {
|
||||
private let playerLayer = AVPlayerLayer()
|
||||
|
||||
override init(frame: NSRect) {
|
||||
super.init(frame: frame)
|
||||
wantsLayer = true
|
||||
let playerLayer = AVPlayerLayer()
|
||||
playerLayer.videoGravity = .resizeAspect
|
||||
layer = playerLayer
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError()
|
||||
}
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
override func layout() {
|
||||
super.layout()
|
||||
(layer as? AVPlayerLayer)?.frame = bounds
|
||||
// 确保 playerLayer 作为 sublayer 存在(不要在 init 中 addSublayer,
|
||||
// 因为 wantsLayer=true 后 layer 可能还没创建)
|
||||
if playerLayer.superlayer !== layer {
|
||||
layer?.addSublayer(playerLayer)
|
||||
}
|
||||
playerLayer.frame = bounds
|
||||
}
|
||||
|
||||
var player: AVPlayer? {
|
||||
get { (layer as? AVPlayerLayer)?.player }
|
||||
set { (layer as? AVPlayerLayer)?.player = newValue }
|
||||
get { playerLayer.player }
|
||||
set { playerLayer.player = newValue }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user