Я воспроизвел проблему с некоторыми изменениями вашего примера с 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 или намеренно перенесите ссылку на ресурс в асинхронную очередь), и это все, что я могу сделать.