Lazy calculated variable
From the docs:
Modifying Value Types from Within Instance Methods
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.
However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends. The method can also assign a completely new instance to its implicit self property, and this new instance will replace the existing one when the method ends.
So then, a struct
cannot modify itself unless the function doing the modification is flagged as mutating
. This means you need to define a proper get
function for the property.
var magnitude: Double {
mutating get {
if magnitudeActual < 0.0 {
NSLog("Recalc") // just to make sure it's caching the result properly.
magnitudeActual = sqrt(x * x + y * y)
}
return magnitudeActual
}
}
Now we can do this
var v = Vector(3, 4)
v.magnitude // Recalc 5
v.magnitude // 5
v.x = 5
v.y = 12
v.magnitude // Recalc 13
Swift function vs lazy var vs computed property - difference?
lazy var
s are actually stored properties, so you can't put it in extensions or anywhere stored properties are not allowed.- The getter for computed properties is run every time you refer to that property. This can be significant especially if the getter is time-consuming or has side-effects to other parts of the code.
- The getter for
lazy var
s are only run when the property is first referred to and never again. lazy var
s are variables. You can mutate them.- Computed properties can optionally have a setter, so sometimes they are read-only.
- Using a function like that is very similar to a read only computed property. You just have to add
()
when getting its value.
Is it correct to convert from computed property to lazy variable under constant struct?
Notice that getting a lazy property potentially changes the struct value. Its state could change from "the lazy property has not been computed" to "the lazy property has been computed". This is why the getters of lazy properties are mutating
.
A let
constant does not allow its value to change, so you can't use mutating
members on it. var
s do, so that's why using a var
makes your code compile.
Changing Rect
to a class makes it a reference type. This means that the let
constant shape
stores a reference to an instance of Rect
, and this reference cannot be changed (e.g. you can't do shape = someOtherRect
afterwards). However, the instance of Rect
can still be changed (remember that the let
constant is only storing a reference to it), and so getting a lazy property is fine on shape
, when Rect
is a class. Doing this will only potentially change the instance of Rect
.
In my opinion, it is not appropriate to change Rect.center
to a lazy property. After the first use of its getter, center
will stay unchanged even if you change origin
or size
:
var shape = Rect()
print(shape.center)
shape.size = Size(width: 100, height: 100)
// now shape.center is incorrect!
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.
Why lazy variables / computed properties, not just methods
Answer: Lazy variables exist to cut on loading time (i.e. for optimization reasons). Some variables aren't needed immediately unless explicitly called for. And they, like Math.pi
might take a while to compute. So the application may start computing them in a background thread when the most important user-initiated operations have finished.
As for computed properties, I think it's more of a language decision. Objective-C introduced properties a long time ago, and they've always been technically 'computed' (with getters and setters, be it automatically synthesized / explicitly implemented), and it was (and is) a wildly accepted feature. Why? I don't know for sure. Maybe there isn't 'one clear answer' to this question. Like I said, I believe it's a language decision, not something that can be rationalized completely.
For me personally, it feels more intuitive to both read and write a property using the same .property
syntax, rather than property()
and setProperty(_: Property)
functions. This is an abstraction, and language abstractions exist for comfort / ease-of-use. Your examples are highly contrived, and computed properties are used by the thousands across Apple's frameworks. Not each computed property needs to be extremely time consuming, and most of them are in fact practically instant; thus they are abstracted to feel so.
When you think about it, everything is technically computed. Even if you, for example, set an integer value directly to a structure field, there are things that need to happen on a lower scale. These low level computations are abstracted to feel like non-computed, concrete entities, when in reality they are. It all boils down to abstraction.
Swift Lazy Variables that Load more than Once (Computed Properties?)
You can create something similar to the Objective-C method using a computed property backed by an optional variable.
var _fetchedResultsController: NSFetchedResultsController?
var fetchedResultsController: NSFetchedResultsController {
get {
if _fetchedResultsController != nil {
return _fetchedResultsController!
}
//create the fetched results controller...
return _fetchedResultsController!
}
}
Setting a variable to a defined lazy var within a UIView closure cause reference problems
You were intended to use computed property of swift. But didn’t get it right. Your profile label should have been defined as follows.
var profileLabel: UILabel {
get {
let label = UILabel()
label.font = .displayNameLabel
label.textColor = .profileLabel
label.numberOfLines = .numberOfLines
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}
}
Related Topics
Type Check Operator (Is) for Check VS Homogenous Protocol: Why Can This Be Done for Optionals
Why Is Deinit Not Called Until Uiview Is Added to Parent Again
Custom Uitabbar Unselected Item's Color
Swift Protocol as Generic Parameter
How to Set an Initial Value for @Nsmanaged Property Pfobject Subclass
Swiftui Multiline Text Background Color
Distributednotificationcenter - How to Pass Data Between Applications
Swift Vscode Class in Other File Not Recognized
Call Function When If Value in Nsuserdefaults.Standarduserdefaults() Changes
How to Make a Function Complete Before Calling Others in an Ibaction
Spritekit: Howto Make Holes in Layer with Blendmode
How to Implement a 'Next' Property to a Caseiterable Enum in Swift
Are Built-In Intrinsic Functions Available in Swift 3
Can't Create Default Closure Parameter in Array Extension Method in Swift
Firebase iOS Receive Data from Push Notification
Adding Animation to Tabviews in Swiftui When Switching Between Tabs
Provide Simple Method to Get Current Speed (Implementing Speedometer)
How to Hide the Back Button from the Status Bar on the Apple Watch