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:
yumoqing 2026-06-22 00:47:05 +08:00
parent aa19ab9799
commit f14d47b5c8
2 changed files with 14 additions and 10 deletions

View File

@ -388,12 +388,13 @@ final class PlayerBridge: ObservableObject {
/// app 退 /// app 退
func cleanup() { func cleanup() {
player.pause()
if let obs = timeObserver { player.removeTimeObserver(obs); timeObserver = nil } if let obs = timeObserver { player.removeTimeObserver(obs); timeObserver = nil }
itemStatusObserver?.invalidate(); itemStatusObserver = nil itemStatusObserver?.invalidate(); itemStatusObserver = nil
playbackStatusObserver?.invalidate(); playbackStatusObserver = nil playbackStatusObserver?.invalidate(); playbackStatusObserver = nil
if let token = endObserverToken { NotificationCenter.default.removeObserver(token); endObserverToken = nil } if let token = endObserverToken { NotificationCenter.default.removeObserver(token); endObserverToken = nil }
player.pause() // replaceCurrentItem(with: nil)
player.replaceCurrentItem(with: nil) // playerItem player CoreMedia
} }
deinit { deinit {

View File

@ -54,26 +54,29 @@ struct VideoPlayerRepresentable: NSViewRepresentable {
} }
class PlayerNSView: NSView { class PlayerNSView: NSView {
private let playerLayer = AVPlayerLayer()
override init(frame: NSRect) { override init(frame: NSRect) {
super.init(frame: frame) super.init(frame: frame)
wantsLayer = true wantsLayer = true
let playerLayer = AVPlayerLayer()
playerLayer.videoGravity = .resizeAspect playerLayer.videoGravity = .resizeAspect
layer = playerLayer
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) { fatalError() }
fatalError()
}
override func layout() { override func layout() {
super.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? { var player: AVPlayer? {
get { (layer as? AVPlayerLayer)?.player } get { playerLayer.player }
set { (layer as? AVPlayerLayer)?.player = newValue } set { playerLayer.player = newValue }
} }
} }
#endif #endif