我试图从 AVAssetTrack
对象获取 asset
属性,但有时它是 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
// [...]
}
}
我发现的是:
- 无论我是在设备上运行还是在 模拟器。
- 这似乎不是视频/videoURL 的问题。 该视频是主包的一部分,我尝试了 .mp4 和 .mov 文件,我通过 AVPlayerViewController。
这是一个有效的 demo project .
我也想知道:为什么 AVAssetTrack
的 asset
属性是可选的? (所有!!其他属性是非可选的)
注意:在阅读 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 open var asset: AVAsset? { get }
这就是你的答案。该轨道对其 Assets 的引用很弱。如果我们将轨道向下传递到异步队列中,并且我们不将 Assets 本身与我们一起使用,那么弱引用就会消失, Assets 就会丢失——在调试构建中。
希望这对您有所帮助。您可能正在等待我就这是否构成错误做出一些宏大的结论性声明,但我不会,抱歉。我提供了两种解决方法(使用 Release 版本,或者故意将 Assets 引用带入异步队列),这是我所能做到的。
关于ios - AVAssetTrack 的 Assets 属性有时为零,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55710126/