AVAssetTrack 的资产 属性 有时为零

Asset property of AVAssetTrack is nil sometimes

我正在尝试从 AVAssetTrack 对象中获取 asset 属性,但有时是 nil。似乎只有在我使用 Dispatch.main.async.

后才会出现问题

根据documentation,需要使用loadValuesAsynchronously(forKeys:, completion:)避免阻塞主线程,加载完成后return到主线程。

let asset = AVURLAsset(url: videoInAppBundleURL)
let track = asset.tracks(withMediaType: .video).first!
assert(track.asset != nil) // passes
track.loadValuesAsynchronously(forKeys: [#keyPath(AVAssetTrack.asset)]) {
    assert(track.asset != nil) // passes
    DispatchQueue.main.async {
        assert(track.asset != nil) // FAILS
        // [...]
    }
}

我发现的是:

这是一个有效的 demo project

我也想知道:为什么AVAssetTrackasset 属性是可选的? (所有!!其他属性是非可选的)

注意:在阅读 Matt 的有用评论和进一步调查后编辑了这个问题。

我重现了这个问题,对您的 github 示例进行了一些调整,如下所示:

    let asset = AVURLAsset(url: videoInAppBundleURL)
    let tracksKey = #keyPath(AVAsset.tracks)
    asset.loadValuesAsynchronously(forKeys: [tracksKey]) {
        let track = asset.tracks(withMediaType: .video).first!
        DispatchQueue.main.async {
            assert(track.asset != nil) // fails
        }
    }

好的,但现在仔细看我表演一个惊人的把戏:

    let asset = AVURLAsset(url: videoInAppBundleURL)
    let tracksKey = #keyPath(AVAsset.tracks)
    asset.loadValuesAsynchronously(forKeys: [tracksKey]) {
        let track = asset.tracks(withMediaType: .video).first!
        DispatchQueue.main.async {
            print(asset) // <-- amazing trick
            assert(track.asset != nil) // passes!
        }
    }

哇!我所做的只是添加了一个 print 语句——现在突然之间完全相同的断言通过了。这实际上与您的原始陈述(您后来将其删除)平行,即“使用调试器单步执行代码时,有时问题消失了。”

所以,现在,我的怀疑被彻底激起,我做了一件令人难以置信的聪明事(即使我自己这么说)。我删除了 print(asset),但我将方案的配置从 Debug 切换为 Release。很快,断言仍然通过。

所以你发现的是编译器的一个怪癖——我敢称之为错误吗?

等等,还有更多。您很合理地问为什么 asset 是可选的。因为是weak:

weak open var asset: AVAsset? { get }

这就是你的答案。该曲目对其资产的引用很弱。如果我们将轨道向下传递到异步队列中,并且我们 而不是 将资产本身与我们一起使用,那么弱引用就会消失,资产就会丢失 — 在调试版本中。

希望这对您有所帮助。您可能正在等待我就这是否构成错误做出一些宏大的结论性声明,但我不会,抱歉。我提供了两种解决方法(使用 Release 构建,或故意将资产引用向下放入异步队列),这是我所能做到的。