Lazy Readonly Property in Swift

Lazy readonly property in Swift

If readonly and private are synonyms for you in this specific case, then you can make the setter private by explicitly declaring it:

private(set) lazy var foo : Int = { return 42 }()

That's a good compromise between immutability and laziness.

How to create an immutable lazy property?

In short, that's not possible. As per Swift documentation:

Constant properties must always have a value before initialization
completes, and therefore cannot be declared as lazy.

Link: Swift lazy documentation

You can declare child as private(set) so it can only be changed from inside your class.

Swift - why can't lazy properties be readily made read only

From Apple documentation:

You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.

But if you want do get a value lazy from a class/struct and want to be sure that nobody can write back to that value then simply use a func with a return value. Or like milos said - a private(set) on a lazy property. (which is not the same thing) So somehow everything is possible. :)

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
}()

}

What are the potential repercussions of a lazy property getting initialised more than once?

(See my comment to rmaddy's answer regarding my concern about thread-safety on writing the pointer itself. My gut is that memory corruption is not possible, but that object duplication is. But I can't prove so far from the documentation that memory corruption isn't possible.)

Object duplication is a major concern IMO if the lazy var has reference semantics. Two racing threads can get different instances:

  • Thread 1 begins to initialize (object A)
  • Thread 2 begins to initialize (object B)
  • Thread 1 assigns A to var and returns A to caller
  • Thread 2 assigns B to var and returns B to caller

This means that thread 1 and thread 2 have different instances. That definitely could be a problem if they are expecting to have the same instance. If the type has value semantics, then this shouldn't matter (that being the point of value semantics). But if it has reference semantics, then this very likely be a problem.

IMO, lazy should always be avoided if multi-threaded callers are possible. It throws uncertainty into what thread the object construction will occur on, and the last thing you want in a thread-safe object is uncertainty about what thread code will run on.

Personally I've rarely seen good use cases for lazy except for where you need to pass self in the initializer of one of your own properties. (Even then, I typically use ! types rather than lazy.) In this way, lazy is really just a kludgy work-around a Swift init headache that I wish we could solve another way, and do away with lazy, which IMO has the same "doesn't quite deliver what it promises, and so you probably have to write your own version anyway" problem as @atomic in ObjC.

The concept of "lazy initialization" is only useful if the type in question is both very expensive to construct, and unlikely to ever be used. If the variable is actually used at some point, it's slower and has less deterministic performance to make it lazy, plus it forces you to make it var when it is most often readonly.

Swift: why lazy, computed property, and property observer can not be let

Lazy properties : You must always declare a lazy property as a variable (with the var keyword), because its initial value might not be retrieved until after instance initialization completes. Constant properties must always have a value before initialization completes, and therefore cannot be declared as lazy.

computed property : whereas computed properties calculate (rather than store) a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.

property observer : property observers is to monitor changes in a property’s value, if you define it let then how you can monitor changes because let is one type of constant which you can not change after init.

Swift must use lazy for stored property when use self in closure?

When you are declaring label with lazy initial value is not calculated until the first time it is used.
so the probably the Instantiates views was completed.

But the concept of using let in Swift, variables which are let have to be initialized before you can use self.

Using lazy var means that the compiler can verify that the value assigned to label won't be accessed before self is a valid object, because it won't be possible to call label until all other members of the class have been initialized.

Initialization of properties in a class, lazy properties in specific?


  1. Yep, that is correct.
  2. Yep, that is also correct.
  3. What is being initialized is the constant someClass. Declaration is the introduction of a new named value into your program. You declare the name of the constant (or variable) and identify its type like this:

    let someClass: SomeClassWithLazyVar

But at that point it still hasn't been initialized. You initialize the constant by assigning it a value:

someClass = SomeClassWithLazyVar()

The vast majority of the time (especially with constants) you declare the constant and initialize it at the same time:

let someClass = SomeClassWithLazyVar()

Whether or not you need to pass arguments inside the parentheses depends on the initializer for the object that you are creating. I'm assuming that SomeClassWithLazyVar has an initializer that takes no arguments, like this:

init() { }

Lazy let? Unswifty workaround in structs

Maybe you could use lazy var after all:

lazy private(set) var perimeter: Int = {
...
}()

A read-only var gets you closer to the desired let semantics.



Related Topics



Leave a reply



Submit