SwiftUI VideoPlayer 泄漏(@状态管理问题)

问题描述 投票:0回答:1

我正在尝试创建循环视频播放器视图。根据Apple的docs,我这样做了:

struct
VideoPlayerView: View
{
    let video           :   Video
    let isPlaying       :   Bool
    
    
    init(video: Video, isPlaying: Bool)
    {
        Self.logger.info("VideoPlayerView.init: \(video.name)")
        
        self.video = video
        self.isPlaying = isPlaying
        
        if let url = self.video.url
        {
            let playerItem = AVPlayerItem(url: url)
            self._player = State(initialValue: AVQueuePlayer(playerItem: playerItem))
            self._looper = State(initialValue: AVPlayerLooper(player: self.player!, templateItem: playerItem))
        }
    }
    
    var
    body: some View
    {
        let _ = print("VideoPlayerView body requested: “\(self.video.name)” [\(self.namespace)]", terminator: " -- ")
        let _ = Self._printChanges()
        
        VStack
        {
            if let player = self.player
            {
                VideoPlayer(player: player)
            }
            else
            {
                Text("Unable to create player.")
            }
        }
        .onAppear()
        {
            Self.logger.info("Player appeared: \(self.video.name)")
            self.isPlaying ? self.player?.play() : self.player?.pause()
        }
        .onDisappear()
        {
            Self.logger.info("Player disappeared: \(self.video.name)")
            self.player?.pause()
            self.player?.seek(to: .zero)
        }
        .onChange(of: self.isPlaying)
        { inOld, inNew in
            Self.logger.info("isPlaying changed: \(inNew)")
            inNew ? self.player?.play() : self.player?.pause()
        }
    }
    
    @State  private var player          :   AVQueuePlayer?
    @State  private var looper          :   AVPlayerLooper?
    
    @Namespace private var namespace
    
    static  let logger          =   Logger(subsystem: "<>", category: "VideoPlayerView")
}

我根据当前选择将其实例化为

NavigationSplitView
细节的一部分:

        detail:
        {
            let sortedSelection = self.selection.sorted(using: SortDescriptor(\.lastViewed))
            if sortedSelection.count > 0
            {
                ScrollView
                {
                    VStack
                    {
                        ForEach(sortedSelection)
                        { video in
                            VideoPlayerView(video: video, isPlaying: self.isPlaying)
                                .id(video)
                                .aspectRatio(4.0/3.0, contentMode: .fit)
                        }
                    }
                }
            }
            else
            {
                Text("Select an Item")
            }
        }
@Model
final
class
Video
{
    var name            :   String
    var bookmark        :   Data
    var lastViewed      :   Date?
    
    var
    url: URL?
    {
        <get url from bookmark>
    }

这种方法存在问题:

  • 如果在选择另一个视频时正在播放一个视频,则第一个视频的视图会消失,但您可以听到音频继续播放(直到我在
    pause()
    中拨打
    .onDisappear()
    ,无论如何)。
  • 每次发生变化时,例如视频是否正在播放,此代码都会创建一套新的 AVFoundation 对象。我实际上不知道
    @State
    在这种情况下会做什么。由于该视图的状态已经存在,它是否只是丢弃新的初始值?
  • 看看内存消耗和线程创建,答案是否定的。每次我停止并开始播放视频时,内存都会增加,并且会创建一些新线程。

我想不出更好的方法来管理这个玩家状态。在

.onAppear()
中分配状态并将其设置为
nil
中的
.onDisppear()
不会改变行为;内存仍然增长并且线程继续创建。

swiftui avfoundation avkit
1个回答
0
投票

不幸的是,苹果的示例代码存在重大缺陷,就像其他框架团队尝试 SwiftUI 时常见的那样。

@State 不能与像 AVPlayer 这样的类一起使用,这本身就是内存泄漏。

isPlaying 的@State 违反了 SwiftUI 的单一事实来源设计,因为它试图将状态复制到播放器中,而不是使用播放器作为播放的事实来源。

尝试将问题分解为单独的视图结构。

© www.soinside.com 2019 - 2024. All rights reserved.