From 6bbd86006aac97eb746d0e5a2de5d5c630816ac7 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Mon, 22 Jun 2026 01:11:40 +0800 Subject: [PATCH] fix: nil AVPlayerLayer.player before closing fullscreen window Root cause: when FullscreenPlayerView deallocates during window close, AVPlayerLayer.player reference is released via autorelease pool drain on main thread. CoreMedia background threads simultaneously access sFigNotificationCenterWeakListenerLinks dictionary (weak listener cleanup), causing use-after-free race condition. Fix: - toggleFullscreen(): set playerView.player = nil BEFORE fw.close() - FullscreenPlayerView.viewWillMove(toWindow: nil): safety net to nil out player when view is removed from window by any means This ensures the AVPlayer reference is released synchronously on the main thread, before any autorelease pool drain can race with CoreMedia internal cleanup threads. --- Sources/PlayerBridge.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Sources/PlayerBridge.swift b/Sources/PlayerBridge.swift index 917a727..e4d081b 100644 --- a/Sources/PlayerBridge.swift +++ b/Sources/PlayerBridge.swift @@ -279,6 +279,11 @@ final class PlayerBridge: ObservableObject { func toggleFullscreen() { #if os(macOS) if let fw = fullscreenWindow { + // 先断开 AVPlayerLayer → player 引用,避免 autorelease pool drain 时 + // 与 CoreMedia 后台线程竞争 sFigNotificationCenterWeakListenerLinks + if let playerView = fw.contentView as? FullscreenPlayerView { + playerView.player = nil + } fw.close() fullscreenWindow = nil isFullscreen = false @@ -445,6 +450,15 @@ class FullscreenPlayerView: NSView { override var acceptsFirstResponder: Bool { true } + // 安全网:视图从窗口移除时主动断开 player 引用 + // 避免 CoreMedia 后台线程与 autorelease pool drain 竞争 + override func viewWillMove(toWindow newWindow: NSWindow?) { + if newWindow == nil { + playerLayer.player = nil + } + super.viewWillMove(toWindow: newWindow) + } + override func mouseDown(with event: NSEvent) { if event.clickCount == 2 { NotificationCenter.default.post(name: .init("ExitFullscreen"), object: nil)