Swift - Lazy Var VS. Let When Creating Views Programmatically (Saving Memory)

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

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.

  1. 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.
  2. You can populate a lazy property with the value of a stored property.
  3. You can use self inside the closure of a lazy property

Swift access lazy var endless loop cause crash

The problem is that your closure to create a table view ends up being recursive.

  • When you set a tableFooterView on the table, it immediately tries to find out if that footer should be drawn / visible / onscreen immediately.

  • To know if the footer should be visible, the table view has to find out how many rows should currently be displayed, so it asks the data source (your view controller) for the number of rows in the the first section

  • Inside your method tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int, you have the line if tableView == hotStockTableView {. This tries to access the lazy hotStockTableView var, which still hasn't completed initializing, since that closure is what is calling this code in the first place

  • So the lazy var closure is kicked off again and recursively goes through the same steps over and over and over until stack overflow and crash.

Fortunately, the solution is easy. If you haven't set the data source yet then adding the table view footer will never call your number of rows data source method. So just change the order of code in your lazy closure to the following:

private lazy var hotStockTableView: UITableView = {
let tableView = UITableView()
print("hotStockTableView ======> \(tableView) \n")
tableView.tableFooterView = UIView()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.delegate = self
tableView.dataSource = self
return tableView
}()

Now your code will work just fine.

Swift - is lazy var thread-safe?

From The Swift Programming Language: Properties:

If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

lazy var is not thread safe. You can use

  • dispatch_once (runs once per lifetime of the app)
  • a constant (let)
  • the nested struct pattern (typically used for singletons)

for thread safety. (See this question for some examples.)

You could also employ your own locking using NSRecursiveLock but that's probably not as efficient as dispatch_once.

How to re-initialize a lazy property from another view?

I would suggest creating a function that loads the data into data and then whenever you need to reload data, simply reassign it.

class DataStore {
lazy var data: [String: String] = loadData()

func readFromDisk() -> Data? {...}

func processData(data: Data) -> [String:String] { ... }

func loadData() -> [String:String] {
guard let data = readFromDisk() else { return [:] }
return processData(data: data)
}
}

let store = DataStore()
let data = store.data // only loaded here
store.data = store.loadData() // reloads the data

If you don't want the loadData function to be exposed, you can also create a separate reloadData function.

class DataStore {
...
func reloadData() {
data = loadData()
}
}

and then instead of doing store.data = store.loadData(), simply call store.reloadData()

Is there any gain in Swift by defining constants instead of variables as much as possible?

In theory, there should be no difference in speed or memory usage - internally, the variables work the same. In practice, letting the compiler know that something is a constant might result in better optimisations.

However the most important reason is that using constants (or immutable objects) helps to prevent programmer errors. It's not by accident that method parameters and iterators are constant by default.

Using immutable objects is also very useful in multithreaded applications because they prevent one type of synchronization problems.

How do I deinit a lazy var in Swift?

To do this, your lazy variable would have to be declared as optional even though you intend to initialize it as soon as the value is needed (i.e. lazily).

class MyObject {

func run() {
print( "MyObject :: run()" )
}

init() {
print( "MyObject :: init" )
}

deinit {
print( "MyObject :: deinit" )
}
}

class MyContext {
lazy var myObject:MyObject? = MyObject()
}

let myContext = MyContext()
myContext.myObject?.run() //< "MyObject :: init"
myContext.myObject = nil //< "MyObject :: deinit"

Also, I disagree with the notion of the "doom and gloom of optionals"—one only need know which of the many available techniques is most convenient and practical way handle them, and how to avoid allowing too many permutations of state to exist among combinations of optional and non-optional values in a given context. Furthermore, an optional is actually exactly what you want here because you intend to nullify it. Employing an optional doesn't mean that a value will be nil for now until you initialize it, but rather that it may be nil at any point in its life, even if it is defined at declaration or any other time beforehand. If avoiding optionals is truly that high of a priority for you, you'd have to take a step back and re-evaluate your architecture to create a situation where you no longer have the need to de-initialize the value.

Lazy stored property in Swift

Mike Buss wrote an article about lazy initialization in Swift http://mikebuss.com/2014/06/22/lazy-initialization-swift/

One example of when to use lazy initialization is when the initial value for a property is not known until after the object is initialized or when the computation of the property is computationally expensive.

Here are two examples for both cases from the post:

In the first example we don't know which value personalized greeting should have. We need to wait until the object is initialized to know the correct greeting for this person.

class Person {

var name: String

lazy var personalizedGreeting: String = {
[unowned self] in
return "Hello, \(self.name)!"
}()

init(name: String) {
self.name = name
}
}

The second example covers the case of an expensive computation. Imagine a MathHelper class which should give you values for pi and other important constants. You don't need to calculate all constants if you only use a subset of them.

class MathHelper {

lazy var pi: Double = {
// Calculate pi to a crazy number of digits
return resultOfCalculation
}()

}

Lazy instantiating a UIDynamicAnimator with referenceView - Swift

In the session video "Building Interruptible and Responsive Interactions", they did exactly this.

The solution is to define animator as an Optional and initialize it inside viewDidLoad.

class ViewController: UIViewController {
var animator : UIDynamicAnimator?
@IBOutlet var gameView : UIView
override func viewDidLoad() {
super.viewDidLoad()
animator = UIDynamicAnimator(referenceView: gameView)
animator!.property = value
...
}
...

I slightly dislike this because future references to animator will all need to unwrap it.

A slightly better approach imo is to define it as an implicitly unwrapped optional. So it would look like this:

class ViewController: UIViewController {
var animator : UIDynamicAnimator!
@IBOutlet var gameView : UIView
override func viewDidLoad() {
super.viewDidLoad()
animator = UIDynamicAnimator(referenceView: gameView)
animator.property = value
...
}
...


Related Topics



Leave a reply



Submit