What is the advantage of a lazy var in Swift
Lazy Stored Property vs Stored Property
There are a few advantages in having a lazy property instead of a stored property.
- The closure associated to the lazy property is executed only if you read that property. So if for some reason that property is not used (maybe because of some decision of the user) you avoid unnecessary allocation and computation.
- You can populate a lazy property with the value of a stored property.
- You can use
self
inside the closure of a lazy property
Swift - Lazy Var vs. Let when creating views programmatically (saving memory)
Whether you will use lazy var
or not depends on your code and its context. It is not bad or good on its own. You have to decide when it is appropriate.
Before you can decide that, you have to know what lazy var
is.
What is lazy var
?
Lazy initialization is a concept where initialization (construction) of variable content is delayed until its first usage. First access to such variable triggers initialization. Since content is not created until variable is used (needed) using lazy initialized variables can save resources.
That is primary drive behind lazy initialization. You don't create something until you need it. That is also logic you will use when deciding whether something should be lazy var
or not.
If you are dealing with views (or anything else) that are always visible (needed) there is little point in using lazy initialization. On the other hand when you are dealing with instances that are not always needed - then using lazy var
is justified.
If your view is always visible in presented view controller, you will not accomplish much by making it lazy. If it is visible only under specific circumstances - for instance when user expands some collapsed panel - then making it lazy makes sense. It will make your view controller load faster and use less memory by default.
As far as thread safety is concerned, lazy var
are not thread safe in Swift.
That means if two different threads try to access the same lazy var
at the same time, before such variable has been initialized it is possible that one of the threads will access partially constructed instance.
You can find more about thread safety in:
Swift - is lazy var thread-safe?
Make "lazy var" threadsafe
Swift's Lazy Var
In your examples the outcomes are not quite the same, in the following ways:
Single instantiation. Each time
buttonPressed()
is called, a newHeavyClass
will be instantiated. This is not the case when using the lazy keyword, which will only create the instance on first access. To match the lazy semantics you would have to check and set ifheavyClass == nil
before each access.Nullability. You must unwrap your
heavyClass
each time you want to use it, either through optional chaining (heavyClass?.doStuff()
) or force unwrapping (heavyClass!.doStuff()
). You are also able to set the variable back tonil
, which would be a compiler error in the first example.
The real wins of lazy variables are when you have multiple places which use the variable. I'm sure you can spot the repetition here:
func buttonPressed() {
if self.heavyClass == nil {
self.heavyClass = HeavyClass()
}
self.heavyClass?.doStuff()
}
func aDifferentButtonPressed() {
if self.heavyClass == nil {
self.heavyClass = HeavyClass()
}
self.heavyClass?.doSomethingElse()
}
This is tidied up using a lazy variable:
func buttonPressed() {
self.heavyClass.doStuff()
}
func aDifferentButtonPressed() {
self.heavyClass.doSomethingElse()
}
Difference between Lazy var and var as-a-closure 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.
struct N {
lazy var a: Int = { print("Setting A"); return 5}();
var b: Int = { print("Setting B"); return 5 }()
}
var n = N()
print(n.a)
print(n.b)
Output:
Setting B
Setting A
5
5
Note how non-lazy b
is initialized first. a
is only initialized when it's accessed. In either case, the initializer for each property is only run once.
Why and when to use lazy with Array in Swift?
lazy
changes the way the array is processed. When lazy
is not used, filter
processes the entire array and stores the results into a new array. When lazy
is used, the values in the sequence or collection are produced on demand from the downstream functions. The values are not stored in an array; they are just produced when needed.
Consider this modified example in which I've used reduce
instead of count
so that we can print out what is happening:
Not using lazy
:
In this case, all items will be filtered first before anything is counted.
[1, 2, 3, -1, -2].filter({ print("filtered one"); return $0 > 0 })
.reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 }
filtered one
filtered one
filtered one
filtered one
filtered one
counted one
counted one
counted one
Using lazy
:
In this case, reduce
is asking for an item to count, and filter
will work until it finds one, then reduce
will ask for another and filter
will work until it finds another.
[1, 2, 3, -1, -2].lazy.filter({ print("filtered one"); return $0 > 0 })
.reduce(0) { (total, elem) -> Int in print("counted one"); return total + 1 }
filtered one
counted one
filtered one
counted one
filtered one
counted one
filtered one
filtered one
When to use lazy
:
option-clicking on lazy
gives this explanation:
From the Discussion for lazy
:
Use the lazy property when chaining operations:
to prevent intermediate operations from allocating storage
or
when you only need a part of the final collection to avoid unnecessary computation
I would add a third:
when you want the downstream processes to get started sooner and not have to wait for the upstream processes to do all of their work first
So, for example, you'd want to use lazy
before filter
if you were searching for the first positive Int
, because the search would stop as soon as you found one and it would save filter
from having to filter the whole array and it would save having to allocate space for the filtered array.
For the 3rd point, imagine you have a program that is displaying prime numbers in the range 1...10_000_000
using filter
on that range. You would rather show the primes as you found them than having to wait to compute them all before showing anything.
Swift lazy var reference cycle
The reason that there isn't is a reference cycle is because the closure isn't stored on a
. If a
stored the closure and the closure contained self then there would be a reference cycle.
For lazy properties, everything after the =
isn't called until the first time you access property2
. When you do, the closure is created, called, freed from memory, and then the value is returned to you. This is why you can get rid of the self.
in the closure since it will never capture self.
Lazy Initialization sounds and works great. Why not always default to using lazy initialization?
Consider this example of a lazy var
:
struct S {
lazy var lazyVar: Int = { /* compute some value */ 0 }()
}
What really happens behind the scenes is something like this:
struct S {
var _lazyVar: Int? = nil
var lazyVar: Int {
mutating get {
if let existingValue = _lazyVar {
return existingValue
}
else {
let newlyComputedValue = /* compute some value */ 0
_lazyVar = newlyComputedValue
return newlyComputedValue
}
}
}
}
As you see, every access of lazyVar
requires a branch, to check if there's a value already, or if it's necessary to compute one for the first time. This adds overhead that easily outweighs the benefit of lazily evaluating values with simple (fast) derivations.
Role of Swift 3 lazy var with no getter
lazy
means that the initial value of stored property is evaluated and assigned on the first access (so this is actually unrelated to getters
or setters which are used with computed properties).
lazy var toto = ... some expression evaluating to an `Int` ...
defines a lazy Int
property.
lazy
can be used to delay the evaluation of the initial value
until the property is needed, e.g. if that evaluation is
"expensive" or has side effects.
But from the language/compiler point of view, the initial value on the right-hand side
can be an arbitrary expression.
It does not matter if it is a constant or a "complicated" expression involving function calls.
lazy var a = 5 // A constant
lazy var b = someFunctionReturningAnInt() // Function call
lazy var c = { _ -> Int in
return 42
}() // Immediately evaluated closure
are all valid definitions. There is no advantage of using lazy
with a constant initial value, but it is valid Swift.
Swift function vs lazy var vs computed property - difference?
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.
- The getter for
lazy var
s are only run when the property is first referred to and never again. lazy var
s are variables. You can mutate them.- Computed properties can optionally have a setter, so sometimes they are read-only.
- Using a function like that is very similar to a read only computed property. You just have to add
()
when getting its value.
When to use ?, !, None, or Lazy?
Not exactly that.
All variables must be initialised before the first use, and all class/struct stored properties must be assigned value in respective initialiser. Optionals are not about being allowed uninitalised at some point, but about being allowed to contain no value, which is represented by nil
, which is still perfectly an initialised stated for such variable. Therefore, if something can not be known at the moment of initialisation then that's probably where you will use some sort of an optional (e.g. delegate for a view).
Implicitly unwrapped optionals is a sort of shorthand for cases when a variable might be empty, but we are absolutely sure that when we will be really using it it will hold an actual value (typical example is a property in a view controller that holds reference to a view).
Forced unwrapping does not convert optional into implicitly unwrapped optional, instead it gives you a value that is there if it's there (i.e. if optional is not nil
), and throws an exception if it's not.
Lazy properties are used in cases when you want to defer their initialisation to a later stage, when the property is actually being used for first time. Usual case is if you need to access an expensive resource to do that (load huge file from disk, download it via network, etc), especially so if there might be cases when such property is not going to be used at all (why loading it from the disk if we will not use it probably?).
Related Topics
How to Hide Labels in iOS-Charts
How to Open Settings App Programmatically
What's the Equivalent to String.Localizedstringwithformat(_:_:) for Swiftui's Localizedstringkey
Uicollectionview Compositionallayout Not Calling Uiscrolldelegate
Keyboard Overlaying Action Sheet in iOS 13.1 on Cncontactviewcontroller
Accessing Mkmapview Elements as Uiviewrepresentable in the Main (Contentview) Swiftui View
Confusion Due to Swift Lacking Implicit Conversion of Cgfloat
How to Remove Spaces from a String in Swift
How to Line Break Long Large Title in iOS 11
Swift - Seeding Arc4Random_Uniform? or Alternative
Rotate an Object in Its Direction of Motion
Method' Is Ambiguous for Type Lookup in This Context, Error in Alamofire
Avaudioengine Crashes When Plug Headphones in or Out
How to Include Assets/Resources in a Swift Package Manager Library
Checking If Textfields Are Empty Swift
How to Pause and Resume Nstimer.Scheduledtimerwithtimeinterval in Swift
Consume Swift Package for Multiple Targets and Platforms in a Project