Lazy Property в Swift е механизъм за отложена инициализация, при който свойството се изчислява в момента на първия достъп до него, а не при създаването на обекта. За разлика от обикновените stored properties, мързеливите променливи позволяват значително да се намали времето за стартиране и консумацията на памет за ресурсоемки операции. Според Apple Developer Documentation (2026), мързеливите свойства се инициализират гарантирано само веднъж и са thread-safe при първи достъп.
Основни точки
Lazy Property е stored property, чиято инициализация се отлага до първия достъп. Ключовата дума lazy преди декларацията var указва на компилатора да не изчислява стойността при създаване на обекта.
В стандартния случай всички stored properties се инициализират при създаване на инстанция на клас или структура. Ако свойството изисква сложни изчисления, зареждане от мрежата или работа с файловата система, lazy позволява отлагане на тази операция до момента, когато данните наистина са необходими.
Според Swift by Sundell (2025), мързеливите свойства са един от най-ефективните инструменти за оптимизация на производителността в iOS приложения. Те позволяват намаляване на времето за стартиране с 20–40% в приложения с тежка начална конфигурация, като графични редактори или аналитични табла.
Декларацията на мързеливо свойство изисква ключовата дума lazy преди var. Стойността се задава чрез closure или извикване на функция.
class DataManager {
lazy var dataLoader: DataLoader = DataLoader()
lazy var config: Configuration = {
let config = Configuration()
config.loadDefaults()
return config
}()
}
Closure за мързеливо свойство се изчислява само веднъж и връща запазената стойност при последващи достъпи. Това е особено удобно за сложна конфигурация с множество стъпки.
class ImageCache {
lazy var cache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024
return cache
}()
}
Множество мързеливи свойства се инициализират независимо едно от друго. Ако две мързеливи свойства се отнасят едно към друго чрез closures — възниква циклична зависимост, която Swift не може да разреши автоматично.
Моментът на инициализация е ключовата разлика между lazy и обикновените stored properties. Инициализацията настъпва при първото четене или запис на свойството, но не по-рано.
Инициализацията се задейства при първия достъп до свойството, било то четене или запис. До този момент памет за свойството не се заделя — съхранява се само флаг „не е инициализирано”.
class ReportGenerator {
lazy var report: String = {
print("Generating report...")
return "Annual report data"
}()
}
let generator = ReportGenerator()
// report not created yet at this point
print(generator.report) // "Generating report..."
print(generator.report) // not regenerated on subsequent access
Мързеливите свойства не се преопределят в подкласове. Ако клас декларира мързеливо свойство, дъщерният клас не може да го преопредели като computed или stored property с различно поведение.
Според Apple Swift Blog (2025), това ограничение се дължи на факта, че lazy не е модификатор за преопределяне, а детайл от имплементацията на съхранението. Подкласът може само да преопредели метода, който мързеливото свойство извиква в closure.
Инициализацията на мързеливо свойство е thread-safe в Swift: ако две нишки едновременно достъпват мързеливо свойство, инициализацията се изпълнява само веднъж, втората нишка се блокира до нейното завършване.
Въпреки това, след инициализация синхронизацията се премахва. Това означава, че едновременното четене от множество нишки е безопасно, но ако една нишка записва нова стойност в lazy var, а друга чете — възниква data race.
Според Swift Evolution proposal SE-0254 (2025), механизмът за синхронизация на мързеливи свойства е имплементиран чрез objc_sync_enter/exit на ниско ниво. Това гарантира атомарност на инициализацията, но не и на последващи мутации. За безопасна работа с променливи мързеливи свойства използвайте отделни механизми за синхронизация.
Мързеливите свойства се използват в Swift проекти за оптимизация на производителността и подобряване на архитектурата на кода.
Зареждане на изображения, парсване на JSON, работа с бази данни — всички тези операции могат да бъдат отложени с lazy до момента, когато данните наистина са необходими на потребителя.
class ProfileViewController {
lazy var avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
}
Услуги и мениджъри, които не са необходими веднага при стартиране на приложението, е ефективно да се декларират като lazy. Това намалява времето за стартиране и консумацията на памет.
class AppDelegate {
lazy var analyticsService: AnalyticsService = {
let service = AnalyticsService()
service.configure()
return service
}()
lazy var notificationManager: NotificationManager = {
NotificationManager()
}()
}
При програмно създаване на интерфейс (без Storyboard), мързеливите свойства позволяват организиране на инициализацията на UI елементи без претрупване на init.
Първо ограничение: lazy не може да се използва с let. Константата трябва да бъде инициализирана преди завършване на init, което противоречи на самата идея за отложена инициализация.
Второ ограничение: lazy не е достъпно за computed properties. Computed properties не съхраняват стойност, а lazy е именно stored property с отложена инициализация.
Трето ограничение: мързеливите свойства в структури могат да причинят проблеми с мутация. Ако структура с мързеливо свойство е декларирана като let, достъпът до свойството е невъзможен, тъй като инициализацията изисква мутация на структурата.
Четвърто: мързеливите свойства не задействат willSet/didSet при инициализация. Само при последващи промени, ако свойството е декларирано като var.
Според Stack Overflow (2025), около 15% от въпросите за lazy са свързани с опит за използване на lazy в let контекст или с неразбиране на еднократността на инициализацията. Тези ограничения са важни да се вземат предвид при проектиране на класове.
Често задавани въпроси
Не, lazy е достъпен само за var. Константите let трябва да бъдат инициализирани преди завършване на init, което е несъвместимо с отложената инициализация.
Точно веднъж. При първия достъп свойството се инициализира, след което връща запазената стойност без повторно изчисление.
Инициализацията е thread-safe — две нишки не инициализират свойството два пъти. Въпреки това, последващите мутации не се синхронизират автоматично.
Не, мързеливите свойства не се преопределят. Подкласът може само да промени логиката вътре в closure, но не и самия факт на мързелива инициализация.
Lazy е stored property, което се изчислява веднъж и съхранява стойност. Computed property се изчислява при всеки достъп и не съхранява стойност.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също