gpt4 book ai didi

ios - 单元格的父vc中的Swift -AVPlayer'KVO导致Xcode卡住

转载 作者:行者123 更新时间:2023-12-01 16:11:52 26 4
gpt4 key购买 nike

我有一个占据整个屏幕的单元,所以一次只有一个可见单元。在单元内部,我有一个AVPlayer。在单元的父vc内,我有一个KVO观察器,它监听"timeControlStatus"。当播放器停止播放时,我在单元格内调用函数stopVideo()停止播放器,并显示重播按钮或播放按钮等。

3个问题:

1-如果视频停止/到达结束(如果我不使用KVO内的DispatchQueue),则在调用单元格的函数时应用崩溃。

2-当视频停止并且我确实在KVO中使用DispatchQueue时,它的观察者一直在观察并且Xcode冻结(没有崩溃)。 KVO内部有一个可无限期打印的打印语句,这就是Xcode冻结的原因。阻止它的唯一方法是杀死Xcode,否则死亡的沙滩球将继续旋转。

3-在KVO中,我尝试使用通知来发送到单元格来代替调用cell.stopVideo()函数,但是单元格内的函数从不运行。

如何解决此问题?

应当指出,在KVO之外,其他所有功能均无法正常工作。我有一个定期的时间观察器,无论何时滚动,视频加载都很好,并且每个单元都可以完美运行,而当我按单元格停止/播放视频时,一切正常。

细胞:

protocol MyCellDelegate: class {
func sendBackPlayerAndIndexPath(_ player: AVPlayer?, currentIndexPath: IndexPath?)
}

var player: AVPlayer?
var indexPath: IndexPath?

var playerItem: AVPlayerItem? {
didSet {
// add playerItem to player
delegate?.sendBackPlayerAndIndexPath(player, indexPath)
}
}

override init(frame: CGRect) {
super.init(frame: frame)

player = AVPlayer()
// set everything else relating to the player
}

// both get initialized in cellForItem
var delegate: MyCellDelegate?
var myModel: MyModel? {
didSet {
let url = URL(string: myModel!.videUrlStr!)
asset = AVAsset(url: url)
playerItem = AVPlayerItem(asset: asset, automaticallyLoadedAssetKeys: ["playable"])
}
}

// tried using this with NotificationCenter but it didn't trigger from parent vc
@objc public func playVideo() {
if !player?.isPlaying {
player?.play()
}
// depending on certain conditions show a mute button, etc
}

// tried using this with NotificationCenter but it didn't trigger from parent vc
@objc public func stopVideo() {
player?.pause()
// depending on certain conditions show a reload button or a play button etc
}

父级vc
MyVC: ViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {

var player: AVPlayer?
var currentIndexPath: IndexPath?
var isObserving = false

func sendBackPlayerAndIndexPath(_ player: AVPlayer?, currentIndexPath: IndexPath?) {

if isObserving {
self.player?.removeObserver(self, forKeyPath: "status", context: nil)
self.player?.removeObserver(self, forKeyPath: "timeControlStatus", context: nil)
}

guard let p = player, let i = currentIndexPath else { return }

self.player = p
self.currentIndexPath = i

isObserving = true
self.player?.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil)
self.player?.addObserver(self, forKeyPath: "timeControlStatus", options: [.old, .new], context: nil)
}

// If I don't use DispatchQueue below the app crashes
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

if object as AnyObject? === player {
if keyPath == "status" {
if player?.status == .readyToPlay {
DispatchQueue.main.async { [weak self] in
self?.playVideoInCell()
}
}
}
} else if keyPath == "timeControlStatus" {

if player?.timeControlStatus == .playing {
DispatchQueue.main.async { [weak self] in
self?.playVideoInCell()
}

} else {

print("3. Player is Not Playing *** ONCE STOPPED THIS PRINTS FOREVER and Xcode freezes but doesn't crash.\n")
DispatchQueue.main.async { [weak self] in
self?.stopVideoInCell()
}
}
}
}
}

func playVideoInCell() {
guard let indexPath = currentIndexPath else { return }
guard let cell = collectionView.cellForItem(at: indexPath) as? MyCell else { return }

cell.playVideo()
// also tried sending a NotificationCenter message to the cell but it didn't trigger
}

func stopVideoInCell() {
guard let indexPath = currentIndexPath else { return }
guard let cell = collectionView.cellForItem(at: indexPath) as? MyCell else { return }

cell.stopVideo()
// also tried sending a NotificationCenter message to the cell but it didn't trigger
}

在注释中,@ matt询问崩溃日志(仅在不使用KVO内部使用DispatchQueue时发生)。我启用了僵尸,但没有给我任何信息。崩溃瞬间发生,然后变成空白。它没有给我任何信息。我必须快速拍摄屏幕快照才能获取照片,否则它在崩溃后立即消失。

enter image description here

enter image description here

在KVO(永远打印的第三个)中,我删除了DispatchQueue,仅添加了 stopVideoInCell()函数。当该函数调用单元格的 stopVideo()函数时, player?.pause暂停会短暂获得 EXC_BAD_ACCESS(代码= 2,地址= 0x16b01ff0)崩溃。

enter image description here

然后,该应用程序终止。在控制台内,唯一打印的内容是:

来自调试器的消息:LLDB RPC服务器已崩溃。崩溃日志
位于〜/ Library / Logs / DiagnosticReports中,并带有前缀
'lldb-rpc-server'。请提交错误并附加最新的崩溃信息
日志

当我进入终端并查看打印出的唯一内容时,是我在整个崩溃中所经历的每一天都出现了一堆 lldb-rpc-server_2020-06-14-155514_myMacName.crash语句。

当使用DispatchQueue时,不会发生这种情况,视频也不会停止,但是KVO中的print语句会永远运行,并且Xcode会冻结。

最佳答案

问题在于,在属性观察器中,您正在更改要观察的属性。那是一个恶性循环,一个无限的递归。 Xcode通过冻结您的应用程序来显示此信息,直到最终您崩溃(讽刺)堆栈溢出。

让我们来看一个更简单,独立的示例。我们的界面中有一个UISwitch。开了如果用户将其关闭,我们希望检测到该问题,然后将其切换回打开。该示例是执行此操作的一种愚蠢方法,但可以完美地说明您面临的问题:

class ViewController: UIViewController {
@IBOutlet weak var theSwitch: UISwitch!
class SwitchHelper: NSObject {
@objc dynamic var switchState : Bool = true
}
let switchHelper = SwitchHelper()
var observer: NSKeyValueObservation!
override func viewDidLoad() {
super.viewDidLoad()
self.observer = self.switchHelper.observe(\.switchState, options: .new) {
helper, change in
self.theSwitch.isOn.toggle()
self.theSwitch.sendActions(for: .valueChanged)
}
}
@IBAction func doSwitch(_ sender: Any) {
self.switchHelper.switchState = (sender as! UISwitch).isOn
}
}

会发生什么事?用户关闭开关。我们观察到,作为 switchState;作为响应,我们将开关切换回On并调用 sendActions。并且 sendActions更改 switchState。但是我们仍然处于观察 switchState的代码中间!因此,我们再次执行此操作,然后再次发生。再一次,它再次发生。无限循环...

您将如何摆脱呢?您需要以某种方式中断递归。我可以想到两种明显的方式。一种是对自己思考,“好吧,我只在乎从On到Off的切换。我不在乎另一种方式。”假设是真的,您可以使用简单的 if解决问题,就像选择使用的解决方案一样:
    self.observer = self.switchHelper.observe(\.switchState, options: .new) {
helper, change in
if let val = change.newValue, !val {
self.theSwitch.isOn.toggle()
self.theSwitch.sendActions(for: .valueChanged)
}
}

我有时会使用的一种更精细的解决方案是,在触发观察者时停止观察,进行任何更改,然后再次开始观察。您必须提前计划以实现该目标,但这有时是值得的:
var observer: NSKeyValueObservation!
func startObserving() {
self.observer = self.switchHelper.observe(\.switchState, options: .new) {
helper, change in
self.observer?.invalidate()
self.observer = nil
self.theSwitch.isOn.toggle()
self.theSwitch.sendActions(for: .valueChanged)
self.startObserving()
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.startObserving()
}

这看起来是递归的,因为 startObserving调用了它自己,但实际上不是,因为它在调用时的作用是配置观察值。直到我们观察到更改,内部花括号中的代码才会运行。

(在现实生活中,我可能会将NSKeyValueObservation设置为该配置中的局部变量。但这只是一点点优雅,对于示例而言并不是必需的。)

关于ios - 单元格的父vc中的Swift -AVPlayer'KVO导致Xcode卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62377105/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com