Object creation is a heavy process. When we create a class object, all the public and private properties of that class are initialised inside the constructor. Every variable inside a class initialisation requires a certain amount of time to allocate the memory on the heap and hold its reference on the stack. The more variables, the more time it may take but since the time is in microseconds or even less, it’s not observable.
Sometimes we don’t need all the objects to be initialised during the class object creation itself.
There can be two reasons for that.
- That object/property/variable is dependent on another object to initialise first and use its reference.
- The flow is such that we need that object only in a certain condition.
In Swift, we have certain features which can help us in delaying that object initialisation as per the requirement.
One way of doing that is using lazy initialization.
Lazy initialisation is also denoted as lazy var
in Swift.
lazy initialisation is a delegation of object creation when the first time that object will be called. The reference will be created but the object will not be created. The object will only be created when the first time that object will be accessed and every next time the same reference will be used.
The basic initialisation of lazy property in a User
struct object by using lazy will look like below
struct User {
let name : String
let age : Int
}
struct Department {
var users : [User]
init(users : [User]) {
print("Department constructor is called")
self.users = users
}
lazy var youngestUser : User? = {
print("Department youngestUser is computed")
return self.users.min(by: {$0.age < $1.age})
}()
}
Let’s try to understand what exactly lazy is
Lazy initialisation is a delegation of property initialisation that will only be called when that property will be used the first time.
Even if we initialise the struct
or class
object, the lazy property will not be set.
Lets see this with an example.
struct User {
let name : String
let age : Int
}
struct Department {
var users : [User]
init(users : [User]) {
print("Department constructor is called")
self.users = users
}
lazy var youngestUser : User? = {
print("Department youngestUser is computed")
return self.users.min(by: {$0.age < $1.age})
}()
}
var engineeringDepartment = Department(users: [
User(name: "Suneet", age: 29),
User(name: "Ballu", age: 20),
User(name: "Agrawal", age: 50)
])
//This will print: Department constructor is called
print(engineeringDepartment.youngestUser ?? "")
//This will print: Department youngestUser is computed
//followed by : User(name: "Ballu", age: 20)
As we can see the lazy property was not initialised even when we initialised the Department
struct object. That only got initialised when we called the lazy property for the first time.
Things to keep in mind while working with lazy property
Can only be used with var
lazy can only be used with var
but it can’t be used with a let
(immutable) property.
struct Department {
//...
lazy let youngestUser : User? = { <e>//'lazy' cannot be used on a let</e>
return self.users.min(by: {$0.age < $1.age})
}()
}
Can be of primitive or non-primitive type
A lazy property can be of primitive or non-primitive type.
struct Department {
//...
lazy var youngestUserAge : Int? = {
return self.users.min(by: {$0.age < $1.age})?.age
}()
lazy var youngestUser : User? = {
return self.users.min(by: {$0.age < $1.age})
}()
}
Can only be used inside a class or struct
Lazy can only be used inside a class
or struct
. We can’t define it in a function or at the root level of a playground file.
lazy var youngestUser : Int = { <e>//Lazy is only valid for members of a struct or class</e>
return 1
}()
The struct or class object accessing lazy property should also be var
The object which is trying to access the lazy property should also be mutable means var
.
struct Department {
//...
lazy var youngestUser : User? = {
print("Department youngestUser is computed")
return self.users.min(by: {$0.age < $1.age})
}()
}
let engineeringDepartment = Department(users: [
User(name: "Suneet", age: 29),
User(name: "Ballu", age: 20),
User(name: "Agrawal", age: 50)
])
print(engineeringDepartment.youngestUser ?? "")
<e>//Cannot use mutating getter on immutable value: 'engineeringDepartment' is a 'let' constant</e>
Please note that it’s not compulsory to create all the objects of that class
or struct
should be var
but in order to access the lazy property, the variable should be var
(mutable) type.
Once the value is computed, it will not change until the constructor is called again
The computation of the lazy property will only happen once. Later the same values will be returned without computation.
var engineeringDepartment = Department(users: [
User(name: "Suneet", age: 29),
User(name: "Ballu", age: 20),
User(name: "Agrawal", age: 50)
])
print(engineeringDepartment.youngestUser ?? "")
//This will print: User(name: "Ballu", age: 20)
engineeringDepartment.users.append(User(name: "John", age: 18))
//or
engineeringDepartment.users = [User(name: "John", age: 18)]
print(engineeringDepartment.youngestUser ?? "")
//This will still print: User(name: "Ballu", age: 20)
As we can see, even if changed values in the users
array or even initialised the array again, the lazy property is still holding the last values.
The lazy property will only be computed again when the Department
constructor will be called on same object else the lazy property will hold the same values.
Few things to remember about lazy property
A lazy property is used to delay the initialisation of property and it will be initialised only when the first time that property will be called but it will only be called once. Later it will return the same values.
Also, it can only be used inside a class
or struct
and the calling object should also be var
.