Lazy Properties in iOS

lazy properties in Swift

Lazy properties are properties being delayed in their creation until we need them. Some properties are rarely used or their creation is expensive. These types of properties should be marked as lazy, so they will be created once we need them.

Using lazy properties is a good sign that you really care about memory especially for mobile apps where memory is limited.

Old days:

In old days, when we’re writing Objective-C, lazy properties were not directly supported in the language and we had to do a trick to achieve lazy properties. You must have seen something like the following in Objective-C:

@interface TestClass ()
@property (nonatomic, strong) NSDateFormatter *dateFormatter;
@end
@implementation TestClass
- (NSDateFormatter *)dateFormatter {
    if (_dateFormatter == nil) {
        _dateFormatter = [[NSDateFormatter alloc] init];
    }
    return _dateFormatter;
}
@end

The idea is to just create a getter function to the property (We do this by creating a function with same name of the property) and then check if the property is nil or not. If its nil, just create it and then return the property.

In lovely Swift:

In Swift, your life is easier. Swift has a direct support of lazy properties by using the keyword lazy modifier before the declaration. Lets see the following example:

class ViewModel {
    lazy var dateFormatter = DateFormatter()
}

DateFormatter objects are ones of the expensive objects in iOS, so it’s better to create it only when we need it. That’s why we have marked it as lazy.

lazy var or lazy let?

Lazy properties must always be declared as variables. Think about it, constants properties must have values during initialization other than lazy properties which may have no value even after initialization waiting for someone to need them to be initialized.

Using lazy with closure:

You may notice using closures in properties declaration like computed properties or lazy properties. To make it crystal clear, lets see together the following different forms of declaring a date formatter property:

class ViewModel {
    var dateFormatter1 = DateFormatter()
    var dateFormatter2: DateFormatter {
        let df = DateFormatter()
return df
}
    lazy var dateFormatter3 = DateFormatter()
    lazy var dateFormatter4: DateFormatter = {
        let df = DateFormatter()
df.dateFormat = "MM, YYYY"
return df
    }()
}

dateFormatter1: is a stored property and the code after equal sign will be executed once after initialization. Every time you access dateFormatter1, same instance will be returned to you.

dateFormatter2: is totally different from example 1 as this property is computed property. Every time you access dateFormatter2, the code after the equal sign will be executed and gives you a totally new instance of DateFormatter.

dateFormatter3: is similar to example 1 but the creation (code after equal sign) will be executed only once after you access the property.

dateFormatter4: the lazy property is initialized using a closure which gives you flexibility to customize the initialized object like setting dateFormat as you see in the example. Think of it as a special kind of computed property that will be computed only once. The code after the equal sign will be executed only once when you access the property.

Do we need weak self in the closure of lazy property?

Check the following example:

class Person {
    let firstName: String
let lastName: String
lazy var fullName: String = {
return "\(self.firstName) \(self.lastName)"
}()
}

Actually its a good question. We became sensitive now while using closures and keep asking ourselves if the closure captures self or not?. Lets figure it out together:

  1. We have two types of closures escaping and noescaping closures. The escaping closures are closures that escape a function (the closure is passed as an argument to a function and is called after the function returns). The escaping closure needs to access self explicitly. The escaping closure needs to retain self and that’s why break the retain cycle by using weak self
  2. If the closure assigned for the lazy property is an escaping closure, we will need to break the cycle by using weak self . So the question is this closure an escaping closure. The answer is NO. When you immediately apply a closure, by default it will be noescape closure.

Conclusion: lazy closure DOESN’T capture self.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.