Difference between computed property and property set with closure
In short, the first is a stored property that is initialized via a closure, with that closure being called only one time, when it is initialized. The second is a computed property whose get
block is called every time you reference that property.
The stored property’s initialization closure is called once and only once, but you can later change the value of the stored property (unless you replace var
with let
). This is useful when you want to encapsulate the code to initialize a stored property in a single, concise block of code.
The computed property’s block, however, is called each time you reference the variable. It’s useful when you want the code to be called every time you reference the computed property. Generally you do this when the computed property needs to be recalculated every time you reference the stored property (e.g. recalculated from other, possibly private, stored properties).
In this case, you undoubtedly want the stored property (the first example), not the computed property (the second example). You presumably don't want a new push behavior object each time you reference the variable.
By the way, in your first example, you internally reference to it being instantiated lazily. If you want that behavior, you must use the lazy
keyword:
lazy var pushBehavior: UIPushBehavior = {
let behavior = UIPushBehavior()
behavior.setAngle(50, magnitude: 50)
return behavior
}()
If, however, the property is static
, it is automatically instantiated lazily.
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.
- 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.
- You can populate a lazy property with the value of a stored property.
- You can use
self
inside the closure of a lazy property
When isn't necessary to initialize lazy variable with lambda execution?
People who do it the second way are making a mistake, that’s all.
The mistake is an easy one. Sometimes (1) a define-and-call initializer is necessary, namely when you need multiple code statements to obtain the initial value of a variable:
let timed : Bool = {
if val == 1 {
return true
} else {
return false
}
}()
Sometimes (2) you need lazy initialization, namely in order to mention self
during property initialization:
lazy var arrow : UIImage = self.arrowImage()
And sometimes (3) you need both together to do both things:
lazy var prog : UIProgressView = {
let p = UIProgressView(progressViewStyle: .default)
p.alpha = 0.7
p.trackTintColor = UIColor.clear
p.progressTintColor = UIColor.black
p.frame = CGRect(x:0, y:0, width:self.view.bounds.size.width, height:20)
p.progress = 1.0
return p
}()
So it is natural out of habit, misunderstanding, or abundance of caution to resort to form 3 when in fact there was only one line and all you needed was form 2. It’s an easy mistake and does no harm.
Property initialization using by lazy vs. lateinit
Here are the significant differences between lateinit var
and by lazy { ... }
delegated property:
lazy { ... }
delegate can only be used forval
properties, whereaslateinit
can only be applied tovar
s, because it can't be compiled to afinal
field, thus no immutability can be guaranteed;lateinit var
has a backing field which stores the value, andby lazy { ... }
creates a delegate object in which the value is stored once calculated, stores the reference to the delegate instance in the class object and generates the getter for the property that works with the delegate instance. So if you need the backing field present in the class, uselateinit
;In addition to
val
s,lateinit
cannot be used for nullable properties or Java primitive types (this is because ofnull
used for uninitialized value);lateinit var
can be initialized from anywhere the object is seen from, e.g. from inside a framework code, and multiple initialization scenarios are possible for different objects of a single class.by lazy { ... }
, in turn, defines the only initializer for the property, which can be altered only by overriding the property in a subclass. If you want your property to be initialized from outside in a way probably unknown beforehand, uselateinit
.Initialization
by lazy { ... }
is thread-safe by default and guarantees that the initializer is invoked at most once (but this can be altered by using anotherlazy
overload). In the case oflateinit var
, it's up to the user's code to initialize the property correctly in multi-threaded environments.A
Lazy
instance can be saved, passed around and even used for multiple properties. On contrary,lateinit var
s do not store any additional runtime state (onlynull
in the field for uninitialized value).If you hold a reference to an instance of
Lazy
,isInitialized()
allows you to check whether it has already been initialized (and you can obtain such instance with reflection from a delegated property). To check whether a lateinit property has been initialized, you can useproperty::isInitialized
since Kotlin 1.2.A lambda passed to
by lazy { ... }
may capture references from the context where it is used into its closure.. It will then store the references and release them only once the property has been initialized. This may lead to object hierarchies, such as Android activities, not being released for too long (or ever, if the property remains accessible and is never accessed), so you should be careful about what you use inside the initializer lambda.
Also, there's another way not mentioned in the question: Delegates.notNull()
, which is suitable for deferred initialization of non-null properties, including those of Java primitive types.
Swift class: declare variables with closures pointing to member functions
Assigning the value inside the initializer cannot work, since you are trying to assign an instance property (or function in your specific case) to another instance property, but in the initializer, neither of them are guaranteed to be initialized.
You can solve your problem by declaring aFunc
as lazy. You won't need an initializer anymore either.
lazy var aFunc: (Int) -> () = f_release
Advantage of computed properties (gettable ones only) vs. stored properties
Computed Properties
var sayGoodMorningToUserComputed: String {
return greeting + username
}
sayGoodMorningToUserComputed
acts just like a function. If a change has been made to greeting
or username
, then sayGoodMorningToUserComputed
will return an up-to-date result that will be the concatenation of the current values.
You would want to use this if you want to ensure your returned value is computed off the latest values of its dependencies (greeting
and username
).
In the case that both dependencies are final
, then it's very likely that the compiler would optimise this computed property into a stored property, because it knows the dependencies can't change
Stored properties
var sayGoodMorningToUserStored = greeting + username
sayGoodMorningToUserStored
is just a variable, with nothing special going on. However, it's only set once, whenever the containing scope is initialized. It's computed once, stored and remains constant until it is overwritten by an external source. As such, if greeting
or username
changes, there will be no effect on sayGoodMorningToUserStored
, because it's been computed from the old values, and stored.
You would want to use this if you want to improve performance by caching the result of a computation whose dependencies are constant.
How do JavaScript closures work?
A closure is a pairing of:
- A function and
- A reference to that function's outer scope (lexical environment)
A lexical environment is part of every execution context (stack frame) and is a map between identifiers (i.e. local variable names) and values.
Every function in JavaScript maintains a reference to its outer lexical environment. This reference is used to configure the execution context created when a function is invoked. This reference enables code inside the function to "see" variables declared outside the function, regardless of when and where the function is called.
If a function was called by a function, which in turn was called by another function, then a chain of references to outer lexical environments is created. This chain is called the scope chain.
In the following code, inner
forms a closure with the lexical environment of the execution context created when foo
is invoked, closing over variable secret
:
function foo() {
const secret = Math.trunc(Math.random() * 100)
return function inner() {
console.log(`The secret number is ${secret}.`)
}
}
const f = foo() // `secret` is not directly accessible from outside `foo`
f() // The only way to retrieve `secret`, is to invoke `f`
Related Topics
Dictionary Becomes Nil When Trying to Pass Back Cllocation Object to iOS App Extension
Getting Reference to a Dictionary Value
Problem with Frameworks in Command Line Tool
Avspeechsynthesizer Isspeaking Not Working in Swift
Extract Reality Composer Scene for Arquicklook
Scenekit Ar Game Fps Getting Low and The Device Getting Hot with Use
Shortest Code to Create an Array of Random Numbers in Swift
How to Upload Images from The Browser to Amazon S3 Using Vapor and Leaf
Using Auto Layout to Orientate Stack Views Vertically in Portrait and Horizontally in Landscape
Implementing Ignoredproperties() on Both a Object Subclass and Its Own Subclass
Kvo Listener Issues in Swift 4
Uibutton Background Color Overlaps Text on Highlight
Swift String Indexing Combines "\R\N" as One Char Instead of Two
How to Refer to a Global Type from Within a Class That Has a Nested Type with The Same Name
Ambiguous Use of Recover Error While Using Promisekit