What are lazy variables and why use in Swift

Subscribe to my newsletter and never miss my upcoming articles

Hello ladies and gentlemen, Leo here.

Today the topic is lazy variables and why it's important you to know. The lazy var are a special type of var that means the variable value isn't calculated until we first use. This brings to table the flexibility of having 1000 items in memory that may not have all properties set, therefore saving memory space.

So let's go to the problem!

Problem

You have an class/struct that have a property that do some expensive(time wise) work to do, that can be a computed property OR another class or struct that are expensive to call.

I'll bring here two examples of how you can improve your code using lazy vars. One with a closure call property lazy var and another with a class instantiation.

The lazy closure call var example

struct FibonacciManager {
    var fiboIndex = 8

    lazy var fibonacciValue: Int = {
        print("calculating fibo")
        return fibonacci(of: fiboIndex)
    }()

    func fibonacci(of num: Int) -> Int {
        if num < 2 {
            return num
        } else {
            return fibonacci(of: num - 1) + fibonacci(of: num - 2)
        }
    }

    mutating func changeFiboIndex(to number: Int) {
        fiboIndex = number
    }
}

And you can test with the code above:

print("old index \(fiboManager.fiboIndex)")
print("First fibo value",fiboManager.fibonacciValue)
fiboManager.changeFiboIndex(to: 9)
print("new index \(fiboManager.fiboIndex)")
print("Second fibo value",fiboManager.fibonacciValue)

The result is:

Screen Shot 2020-10-23 at 11.42.43.png

But you are asking why it doesn't changed the value after the second call. This is because when you use the this closure parameter as above with the lazy in front of it, the compiler delays the calling of the closure until the first time you actually need the value. This is specially good when you have a ton of objects and you don't need to calculate right way some property, so you can delay that calculation saving processing time and ultimately having a bette app experience.

The heavy class example

So imagine now that you have some class that takes time to load. This way every time a class needs to use this expensive class, it would take an extra time and this isn't desirable. Would be better only load the expensive class when we actually need it. Check the below example:

class HeavyClassToLoad {

    var expensiveVar = "cash.txt"

    init() {
        print("Heavy Class initialized")
    }
}

class RandomClassManager {
    lazy var expensiveClass = HeavyClassToLoad()
    var nameList = [String]()
}

As you can see, the manager need the heavy class at some point, but it doesn't need it at the time you instantiate the RandomClassManager. If you test the manager:

let manager = RandomClassManager()

manager.nameList.append("Leo")
manager.nameList.append("Ana")

print(manager.nameList)

The result will be only:

["Leo", "Ana"]

But if you try to use the expensiveClass variable:

manager.expensiveClass.expensiveVar

The result will be as expected:

Screen Shot 2020-10-23 at 12.08.59.png

Only when you first use the expensiveClass variable inside the manager, it will instantiate it. Remember this when you have some heavy loading class variable that don't need in the moment of the class.

Conclusion

Things to remember:

  1. lazy var are always... variables because its initial value might not be retrieved until after instance initialisation is done.
  2. If you are using the closure call example, after the first call, it won't recalculate the value, even if the variables used to calculate de lazy var value had change.
  3. The compiler must know beforehand the type of the lazy var, because the actual value is created by evaluation. Or declaring upfront or assigning the direct class.
  4. Lazy variables ARE NOT thread safe. This means if multiple threads are trying to access the value of a lazy variable there's no guarantee that it will be instantiated only once. BE CAREFUL.

That's it! I hope you all enjoy the content as I did. Any thoughts, comments or/and feedbacks are always welcome.

Thanks for the reading and... That's all folks.

Credit: cover image

No Comments Yet