Computed read-only property vs function in Swift
It seems to me that it's mostly a matter of style: I strongly prefer using properties for just that: properties; meaning simple values that you can get and/or set. I use functions (or methods) when actual work is being done. Maybe something has to be computed or read from disk or from a database: In this case I use a function, even when only a simple value is returned. That way I can easily see whether a call is cheap (properties) or possibly expensive (functions).
We will probably get more clarity when Apple publishes some Swift coding conventions.
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.
difference between a computed property and setting a function to a variable in Swift
The code in the computed property gets executed every time you reference that variable. The code in the property initialized by a closure is only executed once, during initialization.
Read-Only properties
Something like this? (as suggested by @vacawama in the comments)
struct Triangle {
let edgeA: Int
let edgeB: Int
let edgeC: Int
var isEquilateral: Bool {
return (edgeA, edgeB) == (edgeB, edgeC)
}
}
Let's test it
let triangle = Triangle(edgeA: 5, edgeB: 5, edgeC: 5)
triangle.isEquilateral // true
or
let triangle = Triangle(edgeA: 2, edgeB: 2, edgeC: 1)
triangle.isEquilateral // false
What's the point of READ-only variables when you have LET?
Let me try to sum up what the other answers are saying while also adding missing information that I think is critical in order to understand this.
Properties
Properties are simply values that are associated with an object and may be queried in a trivial amount of time without the need (or ability) for parameters like methods have.
Stored Properties
When you create a stored property, whether with let
or var
, the value assigned at any given point in time will be stored in memory, which is why it is called a stored property.
var name = "Matt"
For variables using var
, the value is stored in memory in a way that makes it mutable (editable). You can reassign the value at will and it will replace the previous value stored in memory.
let name = "Matt"
For constants using let
, the value is also stored in memory, but in such a way that it may not be changed after the first time assigning to it.
Computed Properties
Computed properties are not stored in memory. As ganzogo says in the comments, computed properties act similarly to methods, but do not take parameters. When deciding when to use a computed property or a function with no parameters, the Swift API Design Guidelines recommend using a computed property when it will simply create or fetch, and then return the value, provided that this takes a trivial amount of time.
var fullName: String {
return firstName + lastName
}
Here, we assume that firstName
and lastName
are already properties on the object. There is no sense of initialization with this property because it is not stored anywhere. It is fetched on demand every time. That is why there is no sense to doing anything like the following:
var dogName : String {
return "Buster"
}
This has no benefit over a stored property except that no memory will be used in storing the String
"Buster".
In fact, this is a simplified version of computed properties. You will notice that the Swift Language Guide describes the use of both get
and set
in a computed property. set
allows you to update the state of other variables when one sets a computed variable. For example:
var stored: Int
var computed: Int {
get {
return stored + 5
}
set {
stored = newValue - 5
}
}
Some useful applications of this were pointed out by Rajan's answer, for example getting and setting volume from width
, height
, and depth
.
A read-only computed var is just a computed var which specifies only a getter, in which case the get
keyword and brackets are not required.
Read-Only for Access Control
When developing modules such as frameworks, it is often useful to have a variable only be modifiable from within that object or framework and have it be read-only to the public.
private var modifiableItem: String
public var item: String {
return modifiableItem
}
The idea here is that modifiableItem
should only be mutable from within the object that defined it. The private
keyword ensures that it is only accessible within the scope of the object that created it and making it a var
ensures that it may be modified. The public var item
, then, is a computed variable that is exposed to the public that enables anyone to read, but not mutate the variable.
As Hamish notes in the comments, this is more concisely expressible by using private(set)
:
public private(set) var item: String
This is probably the best way to go about it, but the previous code (using a private stored property and public computed one) demonstrates the effect.
Is It Inefficient To Access A Static Property Through Computed Properties In Swift?
As with most optimization questions, it depends on your precise code and the version of the compiler, and also we don't have to guess, we can check.
By "inefficient" I'm going to assume you mean "fails to inline the accessor call."
The TL;DR is: In almost all cases the optimizer will inline this either way. There are corner cases where the accessor version is not inlined, for example if the caller is at the top-level (not inside a function) and the class is non-final. (I don't know why that's a corner-case; it may be an optimizer bug.)
I'm neutral on whether this is a good design. I'm fine with it (and occasionally use this pattern myself). But I certainly wouldn't avoid it out of performance concerns. (In cases where one extra function call would be a problem, you're going to need to hand-optimize anyway.)
The details
As with most optimization/performance questions, it will depend on your exact code and the version of the compiler. As I said, there are some corner cases where this doesn't get optimized. I tested with Swift 5.5.2.
First, I created a test program:
// Avoid the complexity around calling print()
// while ensuring that the variable is not optimized away
@inline(never)
func handle(_ x: Int) {
print(x)
}
// Stick it in a function to avoid the special handling of
// global variables
func f() {
let c = OwnerClass()
let x = OwnerClass.constant1
handle(x)
let y = c.constant1
handle(y)
}
// Make sure to call the function so it's not optimized away
f()
Then I checked it with several version of OwnerClass (I use 12345678 to make it easier to find in the output):
// Class
class OwnerClass {
static let constant1 = 12345678
var constant1:Int { get{ return OwnerClass.constant1 }}
}
// Final class
final class OwnerClass {
static let constant1 = 12345678
var constant1:Int { get{ return OwnerClass.constant1 }}
}
// Struct
struct OwnerClass {
static let constant1 = 12345678
var constant1:Int { get{ return OwnerClass.constant1 }}
}
// Instance constant
class OwnerClass {
static let constant1 = 12345678
let constant1:Int = OwnerClass.contant1
}
The only one that ever had trouble (for example, when I didn't wrap it all in a function), was the non-final class with an accessor.
To see what the optimizer does, I compiled with swiftc -emit-sil -O x.swift
. In all cases, this is what f()
compiles to:
// f()
sil hidden @$s1x1fyyF : $@convention(thin) () -> () {
bb0:
%0 = integer_literal $Builtin.Int64, 12345678 // user: %1
%1 = struct $Int (%0 : $Builtin.Int64) // users: %6, %5, %4, %2
debug_value %1 : $Int, let, name "x" // id: %2
// function_ref handle(_:)
%3 = function_ref @$s1x6handleyySiF : $@convention(thin) (Int) -> () // users: %6, %4
%4 = apply %3(%1) : $@convention(thin) (Int) -> ()
debug_value %1 : $Int, let, name "y" // id: %5
%6 = apply %3(%1) : $@convention(thin) (Int) -> ()
%7 = tuple () // user: %8
return %7 : $() // id: %8
} // end sil function '$s1x1fyyF'
The important thing to note is that the constant 12345678 is inlined into the function as %0 (wrapped into %1), and then it's used twice in %4 and %6 to call handle()
. No calls are made to the accessor. OwnerClass isn't even referenced (the creation of c
is optimized away).
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.
Define a read-only property in Swift
You can use a Computed Property
which (like a method) can be overridden.
class Parent: UIView {
var itemCount: Int { return 0 }
}
class Child: Parent {
override var itemCount: Int { return 1 }
}
Update (as reply to the comment below)
This is how you declared and override a function
class Parent: UIView {
func doSomething() { print("Hello") }
}
class Child: Parent {
override func doSomething() { print("Hello world!") }
}
Whats is better in swift: a function returning a variable or just a getter variable
Does either method have any advantage over the other?
Not internally, no. A computed property is a function, so there is no difference in implementation under the hood.
Related Topics
Decrement Index in a Loop After Swift C-Style Loops Deprecated
What Is the Meaning of the '#' Mark in Swift Language
Convert String to Url (Why Is Resulting Variable Nil)
Swift Closure Async Order of Execution
== Overload for Custom Class Is Not Always Called
Swift: How to Use Preprocessor Flags (Like '#If Debug') to Implement API Keys
What Is _: in Swift Telling Me
Understanding Spritekit Collisionbitmask
How to Check If a Firebase Database Value Exists
Custom Back Button For Navigationview'S Navigation Bar in Swiftui
Deleting a Row from a Uitableview in Swift
How to Pop to the Root View Using Swiftui
Getting "File Not Found" in Bridging Header When Importing Objective-C Frameworks into Swift Project
Overriding Methods in Swift Extensions
Why Optional Constant Does Not Automatically Have a Default Value of Nil