Быстрое табличное представление с видео, воспроизводимым в ячейках табличного представления, утечка памяти

Я делаю приложение для iOS, которое можно сравнить с instagram с потоком видео (без изображений). У меня есть таблица, показывающая настраиваемые ячейки представления таблицы, каждая ячейка представления таблицы содержит видео, воспроизводимое с использованием AVKit. На данный момент у меня загружается 6 ячеек, и для их отображения используется невероятное количество памяти. Как такое приложение, как facebook, instagram или vine, показывает столько видео, не используя безумное количество памяти?

Ниже приведен код, который я сейчас использую.

var cells: [VideoCell] = []
    var cellPostkeys: [String] = []

    @IBOutlet weak var feedTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        feedTableView.delegate = self
        feedTableView.dataSource = self
    }

    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 250
    }

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }


    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return videoURLs.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        let url = videoURLs[indexPath.row]
        if let cell = tableView.dequeueReusableCell(withIdentifier: "testcell") as? VideoCell {
            if (cellPostkeys.contains(url)) {
                let index = cellPostkeys.firstIndex(of: url)
                cells[index!] = cell
            } else {
                cells.append(cell)
                cellPostkeys.append(url)
            }
            cell.playvid(url: url)
            return cell
        } else {
            return VideoCell()
        }
    }
}

И это код для моей пользовательской ячейки представления таблицы

class VideoCell: UITableViewCell {

    @IBOutlet weak var videoView: UIView!


    var player : AVPlayer!
    var avPlayerLayer : AVPlayerLayer!

    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        //
    }

    func stopvid() {
        self.player = nil
        self.avPlayerLayer = nil
    }

    func playvid(url: String) {
        self.player = AVPlayer(url: URL(string: url)!)
        self.avPlayerLayer = AVPlayerLayer(player: self.player)
        self.avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        self.player.automaticallyWaitsToMinimizeStalling = false
        avPlayerLayer.frame = videoView.layer.bounds;
        self.videoView.layer.addSublayer(self.avPlayerLayer)

        self.player.play()

        NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: .main) { [weak self] _ in
            self?.player?.seek(to: CMTime.zero)
            self?.player?.play()
        }
    }

}

Я понимаю, что загрузка и воспроизведение 6 видео одновременно, очевидно, будет невероятно нагружать память, поэтому я попытался воспроизвести только 1 видео, а остальные 5 ячеек были пустыми, но это также использовало около 100+ МБ памяти, когда я начал прокручивать вверх и вниз. Любая помощь будет принята с благодарностью.


person Will Cohen    schedule 14.08.2019    source источник
comment
Этот вопрос помечен как Firebase, но я не вижу в этом вопросе ничего, связанного с Firebase. Кроме того, неясно, где хранятся видео, в каком формате и каков размер каждого видео (как в пикселях, так и в фактическом размере МБ). Это может быть огромным фактором в ответе, поэтому не могли бы вы обновить вопрос, добавив немного более подробной информации об этом?   -  person Jay    schedule 14.08.2019


Ответы (1)


Попробуйте использовать отладчик памяти в Xcode. Проверьте, есть ли какие-либо классы, число которых только увеличивается. Это может быть утечка, потому что вы каждый раз создаете новый AVPlayer и никогда не останавливаете предыдущий.

Вам не нужно каждый раз создавать экземпляр AVPlayer.

private lazy var player: AVPlayer = AVPlayer(playerItem: nil)

private lazy var playerLayer: AVPlayerLayer = {
    let playerLayer = AVPlayerLayer(player: self.player)
    playerLayer.videoGravity = .resizeAspectFill
    return playerLayer
}()

override func awakeFromNib() {
    super.awakeFromNib()
    self.videoView.layer.addSublayer(self.playerLayer)
}

override func layoutSubviews() {
    super.layoutSubviews()
    avPlayerLayer.frame = videoView.layer.bounds
}

Теперь в stopvid() можно либо просто сделать паузу, либо позвонить player.replaceCurrentItem(with: nil)

А в playvid(url: String) вы используете что-то вроде:

let playerItem = AVPlayerItem(url: URL(string: url)!)
player.placeCurrentItem(with: playerItem)
player.play()

Следует отметить, что мне не удалось запустить этот код, так как я сейчас на компьютере с Windows, но, согласно документации, он должен работать.

person Bjarke H. Søndergaard    schedule 15.08.2019