How properly declare a variable in Swift?
I set up a quick test, renaming everything as dogName1
,dogName2
,dogName3
, and dogName4
. Then I added code to try to change each name to "Snoopy".
#2 and #3 didn't build, as the compiler knew they are both read only. (#2, despite being declared as a var
, is set to always return "Charlie".
After commenting those two lines out, I set two breakpoints - on after initializing, one after trying to update.
Finally I tried doing a print
of each one.
Breakpoint #1: #1 and #4 are set to "Charlie", #2 isn't there (because it isn't initialized) and #3 appears as initialized but with no value (because it wasn't called yet. And yes, the ()
at the end initializes something in memory.
Breakpoint #2: #1 and #4 were updated to "Snoopy".
Results of print
: #1 and #4 were "Snoopy", #2 was "Charlie" and #3 was "(Function)".
Conclusion: There's no difference between #1 and #4. Each are declared var
and have a default of "Charlie". #2, is read-only due to the let
and will always return "Charlie". #3? It creates an instance and doesn't build if you try to change it - but I don't know how to use it.
I will update this answer if anyone has more to add about #3.
Method 1 is a standard variable declaration for a String. It has a setter and a getter
var dogName: String = "Charlie"
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
Method 2 is a computed property of type String and is read-only
var dogName: String {
return "Charlie"
}
print(dogName) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
Method 3 is a read-only property of type () -> String, so basically a lambda function.
let dogName = {
return "Charlie"
}
print(dogName) -> "(Function)"
print(dogName()) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
Method 4 is a closure that will be executed when the containing object is initialised. As it is a var
you can replace it with another value
var dogName: String = {
return "Charlie"
}()
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
That being said, as Method 4 is a closure, you can execute other commands in it. Here is an example where you could use this construct to initialise a UILabel:
var dogNameLabel: UILabel = {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
label.text = "Charlie"
return label
}()
To understand the differences let's use a more descriptive example
This is a class Foo
class Foo {
var className = "Foo"
var dogName1 : String { return "Charlie " + className }
let dogName2 = {
return "My name is Charlie"
}
var dogName3 : String = {
var string = "My"
string += " name"
string += " is"
string += " Charlie"
print(string)
return string
}()
}
Now let's create an instance
let foo = Foo()
Method 1 is the stored property
className
with setter and getterlet name = foo.className foo.className = "Bar" print(foo.className) // "Bar"
Method 2 is the computed property
dogName1
only with a getter. It can be used to compute values dynamically.print(foo.dogName1) // "Charlie Bar"
Method 3 is the closure
dogName2
of type() -> String
. It can be assigned to a variable and later be executedlet dogName = foo.dogName2 // assigns the closure but does not return the string. print(dogName()) // "My name is Charlie"
Method 4 is the variable
dogName3
which executes its closure immediately once when an instanceFoo()
is initialized (to be proved by theprint
line)print(foo.dogName3) // "My name is Charlie" but does not execute the closure and print `string` again
There is even a Method 5: If you declare
dogName3
aslazy
lazy var dogName3 : String = {
the closure is not executed until the variable is accessed the first time. The advantage is you can even use
self
in the closure which is not possible in Method 4.