Reputation: 3222
After reading about lazy variables of Swift, I have the following question:
class MainViewController: UIViewController {
lazy var heavyClass = HeavyClass()
func buttonPressed () {
//Use heavyClass here
self.heavyClass.doStuff()
}
}
So you use a lazy var above to optimize the code so heavyClass does not get allocated right away. So upon launching, this would be optimum since heavyClass does not get allocated upon launching time.
However, wouldn't this be the same as above?
class MainViewController: UIViewController {
var heavyClass : HeavyClass?
func buttonPressed () {
//Use heavyClass here
self.heavyClass = HeavyClass()
self.heavyClass!.doStuff()
}
}
Upvotes: 0
Views: 1046
Reputation: 416
There are multiple ways to solve the same problem but I'll talk about a few here. The problem being that you want to only allocate memory when it's needed. There are situations where you might use one way or another plus it's likely somewhat influenced by your coding style. On one hand, let's say the class requires some setup so using lazy might provide better code readability. For example:
lazy var heavyClass: HeavyClass = {
let heavyClass = HeavyClass()
heavyClass.attribute1 = X
heavyClass.attribute2 = X
heavyClass.attribute3 = X
heavyClass.attributeAndSoOn = X
return heavyClass
}()
The point being, all the standard setup is self contained here and you are still getting the benefit of memory allocation only when it's needed. You aren't having to add functions or elongating your action routines (such as buttonPressed).
In another case, you could use optionals but that does add the possibility that the value is nil. Then you have to add logic to handle this.
I've also seen a force-unwrapped model but I don't personally use it myself. It looks like this:
class MainViewController: UIViewController {
var heavyClass : HeavyClass!
func buttonPressed () {
//Use heavyClass here
heavyClass = {
let heavyClass = HeavyClass()
// do your other stuff here
return heavyClass
}()
heavyClass.doStuff()
}
}
It's all a matter of preference. I will say that lazy var's also are not thread safe. Therefore, if you can have multiple threads using that object, one thread has the possibility of accessing a partially constructed object.
Hope this helps!
Upvotes: 1
Reputation: 545
In your examples the outcomes are not quite the same, in the following ways:
Single instantiation. Each time buttonPressed()
is called, a new HeavyClass
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 if heavyClass == 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 to nil
, 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()
}
Upvotes: 8