Swift: The downsides of lazy var (part 2) (2024)

Table of Contents
Problems Benchmark FAQs

2018, Jun 1

Continue from the previous post, we will look into a few problems when working with lazy var.

Problems

Problem 1: not working smoothly with structs

Since the getter of lazy var is mutating, using it with structs sometimes requires extra work. For example, this code below does not compile:

struct Person {private lazy var name = "thuyen"var alias: String { return name } // error: Cannot use mutating getter on immutable}

To make this code work, we have to explicitly declare getter of alias as mutating. But personally, I would rather not make an object mutable just in order to access a specific property.

struct Person {private lazy var name = "thuyen"var alias: String {mutating get { return name }}}

Another approach is to wrap lazy var inside a class LazyBox, like in this article. We will later use this approach as we can handle more issues such as concurrency.

final class LazyBox<T> {private var _value: T?private let compute: () -> Tinit(_ compute: @escaping () -> T) {self.compute = compute}var value: T {if let _value = _value { return _value }let v = compute()_value = vreturn v}}struct Person {private let _name = LazyBox<String> { "thuyen" }var alias: String { return _name.value }}

Problem 2: concurrency

Another problem is that lazy var is not thread-safe which means the closure can get executed multiple times due to accesses from different threads. This is also mentioned in the Apple documentation:

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

To prevent race condition, a simple implementation is to lock every read to the value:

final class LazyBox<T> {private let lock: Synchronizing = NSLock()private lazy var _lazyValue: T = compute()...var value: T {return lock.sync { _lazyValue }}}
extension Locking {func lock()func unlock()}protocol Synchronizing {func sync<T>(execute: () throws -> T) rethrows -> T}extension Locking {func sync<T>(execute: () throws -> T) rethrows -> T {defer { unlock() }lock()return try execute()}}// Let's use NSLock for simplicity. Alternatives: DispatchQueue, pthread_mutex_t, semaphore...extension NSLock: Locking, Synchronizing { }

Now, it works as expected. However, the computation should only occur in the first read access. Locking every read will hurt performance a bit. Therefore, a better implementation is to lock the computation instead.

final class LazyBox<T> {...var value: T {if let _value = _value { return _value }return lock.sync {// Perform computation here...}}}

Note that performing computation more than once is still fine as long as they are synchronous and later computation reuses the result of previous ones.We know for sure that in the next execution inside lock.sync, we definitely have _value computed. Then we can reuse that result, like this:

final class LazyBox<T> {...var value: T {if let _value = _value { return _value }return lock.sync {// Check again if the value is already computed (from the first one get called)if let _value = _value { return _value }let v = compute()_value = vreturn v}}}

Benchmark

I ran a performance test for the two implementations (locking computation only vs. locking every read).The result shows a significant performance gain for the former one (0.002s as compared to 0.065s). The code for the benchmark can be found here.

Swift: The downsides of lazy var (part 2) (2024)

FAQs

What is the disadvantage of lazy property in Swift? ›

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once. Now, it works as expected. However, the computation should only occur in the first read access.

Is lazy var thread-safe in Swift? ›

Thread Safety: lazy vars are not inherently thread-safe. If accessed from multiple threads simultaneously, you need to ensure thread safety manually.

When should I use lazy in Swift? ›

In general, you should only use lazy properties when the initial value of the property is computationally expensive, and when it is not needed until it is actually accessed in the code. Otherwise, you should use a regular property and compute its initial value upfront.

What are lazy variables? ›

A lazy variable in Swift is a variable whose initial value is not calculated until the first time it is accessed. This can be useful when the initial value of a variable is expensive to compute, or when it is not needed until it is actually used in the code.

What is the drawback of lazy loading? ›

What are the disadvantages of lazy loading? Users may request resources faster than expected: For instance, if a user scrolls quickly down a page, they might have to wait for the images to load. This could negatively impact user experience.

What are the disadvantages of lazy initialization? ›

The main disadvantage of lazy initialization is the time penalty associated with each access of the variable caused by the #isNil test. Another disadvantage is that the initialization code is spread around the class and occurs at an unspecified time. On occasion, this can lead to confusion.

Why is Lazy not thread-safe? ›

The Lazy<T> instance is not thread safe; if the instance is accessed from multiple threads, its behavior is undefined. Use this mode only when high performance is crucial and the Lazy<T> instance is guaranteed never to be initialized from more than one thread.

Which is thread-safe late init or lazy? ›

lazy is applicable to both mutable (var) and read-only (val) properties. The lazy delegate is thread-safe by default, ensuring that the initialization code is executed only once, even in a concurrent environment.

Is lazy initialization thread-safe? ›

Thread-Safe Initialization. By default, Lazy<T> objects are thread-safe. That is, if the constructor does not specify the kind of thread safety, the Lazy<T> objects it creates are thread-safe.

Why do we use lazy var in Swift? ›

Lazy variables allow you to delay the initialisation of stored properties. This can be useful to only perform expensive work when it's actually needed. The different between lazy- and computed properties is important in cases you need to have calculations based on the current state of values.

What is the difference between VAR and lazy VAR in Swift? ›

The difference is when the init code for the variable is run. For lazy vars, the init code is run on first access of that variable. For non-lazy vars, it's run when the struct/class is initialized.

Why do we use lazy? ›

lazy should be used when any of your objects are heavy and they take a long time to initialize. Here lazy properties can help to skip that delay which could be caused by the initialization of those objects. As lazy would only initialize the variable when it is called, otherwise it won't be created.

What is the difference between lazy VAR and computed properties? ›

lazy var s are actually stored properties, so you can't put it in extensions or anywhere stored properties are not allowed. The getter for computed properties is run every time you refer to that property. This can be significant especially if the getter is time-consuming or has side-effects to other parts of the code.

Is static lazy Swift? ›

And that's because in Swift, static properties are also implicitly lazy , which means that they don't get initialized until they're actually used!

What is lazy loading in Swift? ›

3 min read. Mar 21, 2023. Photo by David Clode on Unsplash. Lazy loading is a design pattern used to defer the creation of an object or the execution of a task until it is actually needed.

What are the advantages and disadvantages of lazy loading? ›

Overview of the advantages and disadvantages of lazy loading in a table
AdvantagesDisadvantages
Improved performanceUser experience may be affected. For example, backtracking may not be possible if the page structure is not optimal.
Less traffic load for the hostAdditional code when integrating with JavaScript
2 more rows
Jul 17, 2023

What is lazy loading advantages and disadvantages of the same? ›

Lazy loading avoids unnecessary resource downloads or code execution. However it can't help when the user actually requests large or numerous resources. A CDN caches resources and can serve them to users much faster – but it may transmit unnecessary resources which users don't actually need.

What is lazy stored property in Swift? ›

Lazy Stored Properties

A lazy stored property is a property whose initial value isn't calculated until the first time it's used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

How is lazy stored property useful? ›

Lazy property is often used when : The initial value is relatively expensive to create. The value of the property is only known after the object is initialized.

Top Articles
Latest Posts
Article information

Author: Aron Pacocha

Last Updated:

Views: 6166

Rating: 4.8 / 5 (68 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Aron Pacocha

Birthday: 1999-08-12

Address: 3808 Moen Corner, Gorczanyport, FL 67364-2074

Phone: +393457723392

Job: Retail Consultant

Hobby: Jewelry making, Cooking, Gaming, Reading, Juggling, Cabaret, Origami

Introduction: My name is Aron Pacocha, I am a happy, tasty, innocent, proud, talented, courageous, magnificent person who loves writing and wants to share my knowledge and understanding with you.