Lazy Property in Swift is a deferred initialization mechanism where the property is computed at the moment of first access, rather than when the object is created. Unlike regular stored properties, lazy variables can significantly reduce startup time and memory consumption for resource-intensive operations. According to Apple Developer Documentation (2026), lazy properties are guaranteed to be initialized only once and are thread-safe on first access.
Key Takeaways
Lazy Property is a stored property whose initialization is deferred until first access. The keyword lazy before a var declaration tells the compiler not to compute the value when the object is created.
In the standard case, all stored properties are initialized when an instance of a class or structure is created. If a property requires complex computations, network loading, or file system operations, lazy allows delaying this operation until the data is actually needed.
According to Swift by Sundell (2025), lazy properties are one of the most effective performance optimization tools in iOS applications. They can reduce startup time by 20–40% in apps with heavy initial setup, such as graphic editors or analytics dashboards.
Declaring a lazy property requires the lazy keyword before var. The value is set via a closure or function call.
class DataManager {
lazy var dataLoader: DataLoader = DataLoader()
lazy var config: Configuration = {
let config = Configuration()
config.loadDefaults()
return config
}()
}
The closure for a lazy property is computed only once and returns the stored value on subsequent accesses. This is especially convenient for complex configuration with multiple steps.
class ImageCache {
lazy var cache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024
return cache
}()
}
Multiple lazy properties are initialized independently. If two lazy properties reference each other through closures, a cyclic dependency arises that Swift cannot resolve automatically.
The moment of initialization is the key difference between lazy and regular stored properties. Initialization occurs on the first read or write of the property, but not before.
Initialization is triggered on the first access to the property, whether reading or writing. Until that moment, no memory is allocated for the property — only an “uninitialized” flag is stored.
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
Lazy properties cannot be overridden in subclasses. If a class declares a lazy property, a child class cannot override it as a computed or stored property with different behavior.
According to the Apple Swift Blog (2025), this limitation is because lazy is not an override modifier but a storage implementation detail. A subclass can only override the method that the lazy property calls in its closure.
Initialization of a lazy property is thread-safe in Swift: if two threads access a lazy property simultaneously, initialization occurs only once, and the second thread is blocked until it completes.
However, synchronization is removed after initialization. This means concurrent reads from multiple threads are safe, but if one thread writes a new value to a lazy var while another reads, a data race occurs.
According to Swift Evolution proposal SE-0254 (2025), the synchronization mechanism for lazy properties is implemented through objc_sync_enter/exit at a low level. This guarantees atomic initialization but not subsequent mutations. For thread-safe work with mutable lazy properties, use separate synchronization mechanisms.
Lazy properties are used in Swift projects to optimize performance and improve code architecture.
Loading images, parsing JSON, working with databases — all these operations can be deferred using lazy until the data is actually needed by the user.
class ProfileViewController {
lazy var avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
return imageView
}()
}
Services and managers that are not needed immediately at app launch are effectively declared as lazy. This reduces startup time and memory consumption.
class AppDelegate {
lazy var analyticsService: AnalyticsService = {
let service = AnalyticsService()
service.configure()
return service
}()
lazy var notificationManager: NotificationManager = {
NotificationManager()
}()
}
When building the interface programmatically (without Storyboard), lazy properties allow organizing UI element initialization without cluttering the init.
The first limitation: lazy cannot be used with let. A constant must be initialized before init completes, which contradicts the very idea of deferred initialization.
The second limitation: lazy is not available for computed properties. Computed properties do not store a value, while lazy is specifically a stored property with deferred initialization.
The third limitation: lazy properties in structs can cause mutation issues. If a struct with a lazy property is declared as let, access to the property is impossible because initialization requires mutating the struct.
The fourth: lazy properties do not trigger willSet/didSet during initialization. Only on subsequent changes, if the property is declared as var.
According to Stack Overflow (2025), about 15% of questions about lazy are related to attempting to use lazy in a let context or misunderstanding single initialization. These limitations are important to consider when designing classes.
Frequently Asked Questions
No, lazy is only available for var. Constants declared with let must be initialized before init completes, which is incompatible with deferred initialization.
Exactly once. On first access, the property is initialized, after which it returns the stored value without recomputation.
Initialization is thread-safe — two threads will not initialize the property twice. However, subsequent mutations are not automatically synchronized.
No, lazy properties cannot be overridden. A subclass can only change the logic inside the closure, but not the lazy initialization itself.
Lazy is a stored property that is computed once and stores the value. A computed property is computed on every access and does not store a value.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also