Код-ревью Senior Swift Concurrency обновлено
Код-ревью: загрузчик картинок на Swift Concurrency
Вопрос
Найдите проблемы с изоляцией, отменой и производительностью в загрузчике картинок.
final class ImageLoader {
static let shared = ImageLoader()
private var cache: [URL: UIImage] = [:]
func image(for url: URL) async throws -> UIImage {
if let cached = cache[url] { return cached }
let (data, _) = try await URLSession.shared.data(from: url)
let image = UIImage(data: data)!
cache[url] = image
return image
}
}
final class AvatarView: UIImageView {
func show(_ url: URL) {
Task.detached {
let img = try? await ImageLoader.shared.image(for: url)
self.image = img
}
}
}
Коротко
Правильная версия: actor ImageLoader с NSCache и словарём [URL: Task<UIImage, Error] для слияния одинаковых запросов. Во view: @MainActor, обычный Task { } (наследует главный актор), ссылка на задачу в свойстве, cancel() в prepareForReuse и проверка, что URL всё ещё актуален, перед присваиванием.
Что должен найти ревьюер
- Ошибкастрока 3
cacheэто изменяемый словарь в классе, к которому обращаются из разных задач одновременно: гонка данных. Нуженactorили защита блокировкой. - Производительностьстрока 6Два одновременных запроса одного URL оба скачают картинку. Стоит хранить выполняющиеся
Taskпо URL и переиспользовать их. - Ошибкастрока 8
UIImage(data:)!упадёт на битых данных или HTML-странице ошибки. Нужно бросить ошибку. - Производительностьстрока 3Кеш без ограничения размера и без реакции на нехватку памяти. Лучше
NSCacheс лимитом. - Ошибкастрока 18
Task.detachedне наследует MainActor, иself.imageменяется не на главном потоке. - Ошибкастрока 16Задача не сохраняется и не отменяется: при переиспользовании ячейки старая загрузка может перезаписать новую картинку.
Разбор
Правильная версия: actor ImageLoader с NSCache и словарём [URL: Task<UIImage, Error>] для слияния одинаковых запросов. Во view: @MainActor, обычный Task { } (наследует главный актор), ссылка на задачу в свойстве, cancel() в prepareForReuse и проверка, что URL всё ещё актуален, перед присваиванием. Ошибку декодирования картинки превращаем в свою ошибку, а не крэш.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.