Что такое lazy свойства в Swift и в каком контексте их используют?
Что проверяют
Знаете ли вы правила lazy var в Swift и видите ли, где он даёт выигрыш, а где создаёт проблему. Вопрос базовый, поэтому ценится точность: ограничения языка тут важнее общих слов про экономию ресурсов.
Как отвечать
lazy var вычисляется при первом обращении и дальше хранится как обычное свойство. Зачем нужен: дорогая инициализация, которая может не понадобиться, и свойство, зависящее от self (обычное свойство до конца инициализации к self обратиться не может).
final class ProfileViewController: UIViewController {
lazy var avatarView: UIImageView = {
let view = UIImageView()
view.layer.cornerRadius = 24
view.clipsToBounds = true
return view
}()
}Правила, которые стоит назвать вслух: только var, потому что let обязан иметь значение к концу инициализации; вычисляемое свойство ленивым не объявляется; замыкание выполняется ровно один раз, и никакой синхронизации язык при этом не добавляет.
Отдельно отделите lazy var от ленивых последовательностей: array.lazy.map { ... }.first откладывает вычисление самих элементов и не создаёт промежуточный массив. Глобальные переменные и свойства static ленивы всегда и инициализируются потокобезопасно, дописывать к ним lazy не нужно.
Где спотыкаются
Считают lazy var потокобезопасным. Два потока при одновременном первом обращении могут выполнить инициализатор дважды, и один результат просто потеряется. Для разделяемого состояния нужен актор, своя очередь или static let.
Забывают про структуры. Обращение к ленивому свойству мутирует значение, поэтому читать его можно только у изменяемого экземпляра и только из mutating метода; у константы структуры оно недоступно.
Ждут, что свойство пересчитается при смене зависимостей. Оно кэшируется навсегда и остаётся устаревшим. Такому случаю подходит вычисляемое свойство либо обычное необязательное поле, которое сбрасывают вручную.