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 退出前调用
|
/// 主动清理所有观察者,在 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 {
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user