我正在尝试从asset
对象获取AVAssetTrack
属性,但有时是nil
。看来只有在我使用Dispatch.main.async
之后,问题才会出现。
根据documentation,有必要使用loadValuesAsynchronously(forKeys:, completion:)
以避免阻塞主线程,并在加载完成后返回主线程。
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。
我还想知道:为什么AVAssetTrack
的{{1}}属性是可选的? (所有!!其他属性都是非可选的)
注意:在阅读了Matt的有用评论并进行了进一步调查后,已对这个问题进行了编辑。
答案 0 :(得分:1)
我通过对您的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)
,但是将方案的配置从“调试”切换为“发布”。 Presto,断言仍然通过。
那么您发现的是编译器的一个怪癖-敢称它为bug吗?
但是,等等,还有更多。您很合理地问为什么asset
是可选的。这是因为它是weak
:
weak open var asset: AVAsset? { get }
所以有您的答案。这条赛道对其资产的引用很少。如果我们将跟踪传递到异步队列中,并且我们没有不将资产本身与我们一起携带,那么弱引用就会放开,资产会丢失-在调试版本中。
希望这会有所帮助。您可能正在等待我就此是否构成bug做出重大结论,但抱歉,我不会。我提供了两种解决方法(使用Release版本,或故意将资产引用传递到异步队列中),这是我所能做到的。