From f14d47b5c8ce4afcc5bae5e506ec2fd46a0b03a2 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 22 Jun 2026 00:47:05 +0800 Subject: [PATCH] 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 --- Sources/PlayerBridge.swift | 5 +++-- Sources/VideoPlayerView.swift | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Sources/PlayerBridge.swift b/Sources/PlayerBridge.swift index 87cc614..ecba1d5 100644 --- a/Sources/PlayerBridge.swift +++ b/Sources/PlayerBridge.swift @@ -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 { diff --git a/Sources/VideoPlayerView.swift b/Sources/VideoPlayerView.swift index ea0b14e..12efd97 100644 --- a/Sources/VideoPlayerView.swift +++ b/Sources/VideoPlayerView.swift @@ -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