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
// [...]
}
}
我发现的是:
- 无论我是 运行 在设备上还是在
模拟器。
- 看来不是video/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
:
weak open var asset: AVAsset? { get }
这就是你的答案。该曲目对其资产的引用很弱。如果我们将轨道向下传递到异步队列中,并且我们 而不是 将资产本身与我们一起使用,那么弱引用就会消失,资产就会丢失 — 在调试版本中。
希望这对您有所帮助。您可能正在等待我就这是否构成错误做出一些宏大的结论性声明,但我不会,抱歉。我提供了两种解决方法(使用 Release 构建,或故意将资产引用向下放入异步队列),这是我所能做到的。
我正在尝试从 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
// [...]
}
}
我发现的是:
- 无论我是 运行 在设备上还是在 模拟器。
- 看来不是video/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
:
weak open var asset: AVAsset? { get }
这就是你的答案。该曲目对其资产的引用很弱。如果我们将轨道向下传递到异步队列中,并且我们 而不是 将资产本身与我们一起使用,那么弱引用就会消失,资产就会丢失 — 在调试版本中。
希望这对您有所帮助。您可能正在等待我就这是否构成错误做出一些宏大的结论性声明,但我不会,抱歉。我提供了两种解决方法(使用 Release 构建,或故意将资产引用向下放入异步队列),这是我所能做到的。