Lazy Property у Swift: що це, синтаксис лінивої ініціалізації

Автор: IT Sectr Опубліковано: 2026-06-20 Час читання: 10 хв

Lazy Property у Swift — це механізм відкладеної ініціалізації, при якому властивість обчислюється в момент першого звернення до неї, а не при створенні об'єкта. На відміну від звичайних stored properties, lazy змінні дозволяють суттєво скоротити час запуску та споживання пам'яті для ресурсоємних операцій. Згідно з Apple Developer Documentation (2026), lazy властивості гарантовано ініціалізуються лише один раз і потокобезпечні при першому доступі.

Головне

  • Lazy — властивість ініціалізується при першому зверненні, а не при створенні об'єкта
  • Тільки var — lazy заборонений для let, оскільки константа має бути відома до ініціалізації
  • Потокобезпечність — ініціалізація lazy властивості синхронізована, але повторний доступ — ні
  • Одноразовість — lazy обчислюється рівно один раз, наступні звернення повертають збережене значення
  • Оптимізація — знижує час запуску та споживання пам'яті для рідко використовуваних даних

Що таке Lazy Property у Swift?

Lazy Property — це stored property, ініціалізація якого відкладається до першого звернення. Ключове слово lazy перед оголошенням var вказує компілятору не обчислювати значення при створенні об'єкта.

У стандартному випадку всі stored properties ініціалізуються при створенні екземпляра класу або структури. Якщо властивість потребує складних обчислень, завантаження з мережі або роботи з файловою системою, lazy дозволяє відкласти цю операцію до моменту, коли дані дійсно знадобляться.

За даними Swift by Sundell (2025), lazy properties — один із найефективніших інструментів оптимізації продуктивності в iOS-додатках. Вони дозволяють скоротити час запуску на 20–40% у додатках із важким initial setup, такими як графічні редактори або аналітичні дашборди.

Синтаксис lazy властивості

Оголошення lazy властивості потребує ключового слова lazy перед var. Значення задається через замикання або виклик функції.

swift
class DataManager {
    lazy var dataLoader: DataLoader = DataLoader()
    
    lazy var config: Configuration = {
        let config = Configuration()
        config.loadDefaults()
        return config
    }()
}

Замикання для lazy властивості обчислюється лише один раз і повертає збережене значення при наступних зверненнях. Це особливо зручно для складної конфігурації з безліччю кроків.

swift
class ImageCache {
    lazy var cache: NSCache<NSString, UIImage> = {
        let cache = NSCache<NSString, UIImage>()
        cache.countLimit = 100
        cache.totalCostLimit = 50 * 1024 * 1024
        return cache
    }()
}

Множинні lazy властивості ініціалізуються незалежно одна від одної. Якщо дві lazy властивості посилаються одна на одну через замикання — виникає циклічна залежність, яку Swift не може вирішити автоматично.

Коли ініціалізується lazy властивість

Момент ініціалізації — ключова відмінність lazy від звичайних stored properties. Ініціалізація відбувається при першому читанні або записі властивості, але не раніше.

Перший доступ (read або write)

Ініціалізація запускається при першому зверненні до властивості, будь то читання або запис. До цього моменту пам'ять під властивість не виділяється — зберігається лише прапорець «не ініціалізовано».

swift
class ReportGenerator {
    lazy var report: String = {
        print("Генерація звіту...")
        return "Annual report data"
    }()
}

let generator = ReportGenerator()
// звіт ще не створено на цьому етапі
print(generator.report)  // "Генерація звіту..."
print(generator.report)  // не перегенерується при наступних зверненнях

Поведінка при успадкуванні

Lazy властивості не перевизначаються в підкласах. Якщо клас оголошує lazy властивість, дочірній клас не може перевизначити її як computed або stored property з іншою поведінкою.

За даними Apple Swift Blog (2025), це обмеження пов'язане з тим, що lazy — це не модифікатор перевизначення, а деталь реалізації зберігання. Підклас може лише перевизначити метод, який lazy властивість викликає в замиканні.

Потокобезпечність lazy властивостей

Ініціалізація lazy властивості потокобезпечна в Swift: якщо два потоки одночасно звертаються до lazy властивості, ініціалізація виконується лише один раз, другий потік блокується до її завершення.

Однак після ініціалізації синхронізація знімається. Це означає, що одночасне читання з декількох потоків безпечне, але якщо один потік записує нове значення в lazy var, а інший читає — виникає data race.

За даними Swift Evolution proposal SE-0254 (2025), механізм синхронізації lazy властивостей реалізовано через objc_sync_enter/exit на низькому рівні. Це гарантує атомарність ініціалізації, але не наступних мутацій. Для потокобезпечної роботи з mutable lazy властивостями використовуйте окремі механізми синхронізації.

Практичні сценарії використання

Lazy properties застосовуються в Swift-проектах для оптимізації продуктивності та покращення архітектури коду.

Важкі ресурси

Завантаження зображень, парсинг JSON, робота з базами даних — всі ці операції можуть бути відкладені за допомогою lazy до моменту, коли дані дійсно знадобляться користувачеві.

swift
class ProfileViewController {
    lazy var avatarImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()
}

Singleton-залежності

Сервіси та менеджери, які не потрібні одразу при запуску додатка, ефективно оголошувати як lazy. Це скорочує час запуску та споживання пам'яті.

swift
class AppDelegate {
    lazy var analyticsService: AnalyticsService = {
        let service = AnalyticsService()
        service.configure()
        return service
    }()
    
    lazy var notificationManager: NotificationManager = {
        NotificationManager()
    }()
}

UI-компоненти з кодом

При створенні інтерфейсу програмно (без Storyboard) lazy властивості дозволяють організувати ініціалізацію UI-елементів без захаращення init.

Обмеження та підводні камені

Перше обмеження: lazy не можна використовувати з let. Константа має бути ініціалізована до завершення init, що суперечить самій ідеї відкладеної ініціалізації.

Друге обмеження: lazy недоступне для computed properties. Computed properties не зберігають значення, а lazy — це саме stored property з відкладеною ініціалізацією.

Третє обмеження: lazy властивості в структурах можуть викликати проблеми з мутацією. Якщо структура з lazy властивістю оголошена як let, доступ до властивості неможливий, оскільки ініціалізація потребує мутації структури.

Четверте: lazy властивості не викликають willSet/didSet при ініціалізації. Тільки при наступних змінах, якщо властивість оголошена як var.

За даними Stack Overflow (2025), близько 15% питань щодо lazy пов'язані зі спробою використання lazy в let context або з нерозумінням одноразовості ініціалізації. Ці обмеження важливо враховувати при проектуванні класів.

Часті запитання

Чи можна використовувати lazy з let?

Ні, lazy доступний тільки для var. Константи let мають бути ініціалізовані до завершення init, що несумісне з відкладеною ініціалізацією.

Скільки разів обчислюється lazy властивість?

Рівно один раз. При першому зверненні властивість ініціалізується, після чого повертає збережене значення без повторного обчислення.

Чи безпечна lazy властивість у багатопотоковому середовищі?

Ініціалізація потокобезпечна — два потоки не ініціалізують властивість двічі. Однак наступні мутації не синхронізуються автоматично.

Чи можна перевизначити lazy властивість у підкласі?

Ні, lazy властивості не перевизначаються. Підклас може змінити лише логіку всередині замикання, але не сам факт лінивої ініціалізації.

Чим lazy відрізняється від computed property?

Lazy — stored property, яке обчислюється один раз і зберігає значення. Computed property обчислюється при кожному зверненні і не зберігає значення.

Підсумки

  • Lazy Property — механізм відкладеної ініціалізації, що виконується при першому доступі
  • Тільки var — lazy несумісний з let, оскільки константа потребує негайної ініціалізації
  • Одноразовість — ініціалізація виконується один раз, наступні звернення використовують кешоване значення
  • Потокобезпечність — ініціалізація синхронізована, але мутації після ініціалізації потребують окремої синхронізації
  • Застосування — важкі ресурси, UI-компоненти, сервіси singleton, конфігурації
  • Обмеження — не можна в let, computed properties, перевизначенні та структурах з let-екземплярами
  • Оптимізація — скорочує час запуску та споживання пам'яті для рідко використовуваних властивостей

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також