Are Swift Constants Lazy by Default

Are Swift constants lazy by default?

Not exactly. Say you have a class Farm and inside Farm there is horse property.

class Farm {
let horse = Horse()
}

In this case horse property is initialized when class instance initialized. If you make it lazy you have to make it mutable too.

class Farm {
lazy var horse = Horse()
}

In this case, horse property is initialized when it is accessed the first time. And later when it is accessed again it returns the same instance again instead of reinitializing it. But since it is a mutable property you can assign a new instance of Horse to it. After you assign it new value it will return this new value whenever it is accessed.

EDIT: If let horse = Horse() is defined in global space then it is lazily created at first access.

Swift: why aren't all variables lazy by default?

One reason might be that lazyness is not well-suited for situations where you want control when the evaluation happens. this is relevant in cases where the work being done in the assignment has side effects.

Although this pertains to closure, this blog post by stuart sierra explains this idea very well, and I think it applies equally in any language.

Swift static property initilizers are lazy why I could declared it as a constant

Neuburg M. is drawing a distinction between static properties and instance properties. You are pretending to ignore that distinction. But you cannot ignore it; they are totally different things, used for different purposes.

In this code:

class Person { // let's declare a static property
static let firstNaID = "First Name"
}

... firstNaID is already lazy. But now try to do this:

class Person { // let's declare an instance property
lazy let firstNaID : String = "First Name" // error
}

You can't; as things stand (up thru Swift 3.1), you have to say lazy var instead — and when you do, you get a lazy instance property.

Your static let declaration thus doesn't accomplish what lazy let wanted to accomplish, because a static property is not an instance property.

If global properties are always computed lazily, and local properties are never, what does the lazy modifier modify?

"Local constants and variables" in that provided excerpt refer to local scope constants and variables, as in local variables of a function. They do not refer to properties of objects, which can be lazy, if they are marked with the lazy keyword.

//global, declared outside of a class/struct
//error is "Lazy is only valid for members of a struct or class
lazy var label: UILabel = {
var tempLabel: UILabel = UILabel()
tempLabel.text = "hi"
return tempLabel
}()

class SomeClass : NSObject {
//non-lazy instance property
var x = 3

//lazy instance property
lazy var label: UILabel = {
var tempLabel: UILabel = UILabel()
tempLabel.text = "hi"
return tempLabel
}()

func doStuff() {
//error is "Lazy is only valid for members of a struct or class
lazy var label: UILabel = {
var tempLabel: UILabel = UILabel()
tempLabel.text = "hi"
return tempLabel
}()
}
}

Lazy Initialization sounds and works great. Why not always default to using lazy initialization?

Consider this example of a lazy var:

struct S {
lazy var lazyVar: Int = { /* compute some value */ 0 }()
}

What really happens behind the scenes is something like this:

struct S {
var _lazyVar: Int? = nil

var lazyVar: Int {
mutating get {
if let existingValue = _lazyVar {
return existingValue
}
else {
let newlyComputedValue = /* compute some value */ 0
_lazyVar = newlyComputedValue
return newlyComputedValue
}
}
}
}

As you see, every access of lazyVar requires a branch, to check if there's a value already, or if it's necessary to compute one for the first time. This adds overhead that easily outweighs the benefit of lazily evaluating values with simple (fast) derivations.

Implicitly lazy static members in Swift

The static property defines a "type property", one that is instantiated once and only once. As you note, this happens lazily, as statics behave like globals. And as The Swift Programming Language: Properties says:

Global constants and variables are always computed lazily, in a similar manner to Lazy Stored Properties. Unlike lazy stored properties, global constants and variables do not need to be marked with the lazy modifier.

This implicitly lazy behavior is because, as the Swift Blog: Files and Initialization says:

it allows custom initializers, startup time in Swift scales cleanly with no global initializers to slow it down, and the order of execution is completely predictable.

They consciously designed it that way to avoid unnecessarily delaying the startup of the app.

If you want to instantiate the static property at some particular point in your app (rather than deferring it to where it's first used), simply reference this static property at that earlier point and the object will be initialized at that time. Given the efforts we put into reducing the latency in starting our apps, you generally wouldn't to want this synchronously during the initial launch of the app, but you can do it wherever you want.


Note that unlike lazy instance properties, the instantiating of globals and static variables is thread-safe.

Lazy Var vs Let

This is the latest scripture from the Xcode 6.3 Beta / Swift 1.2 release notes:

let constants have been generalized to no longer require immediate
initialization. The new rule is that a let constant must be
initialized before use (like a var), and that it may only be
initialized: not reassigned or mutated after initialization.

This enables patterns like:

let x: SomeThing
if condition {
x = foo()
} else {
x = bar()
}

use(x)

which formerly required the use of a var, even though there is no
mutation taking place. (16181314)

Evidently you were not the only person frustrated by this.



Related Topics



Leave a reply



Submit