Massimo Polimeni
Massimo Polimeni

Reputation: 4906

How properly declare a variable in Swift?

I found quite interesting these different ways to declare a variable in Swift:

// METHOD 1
var dogName: String = "Charlie"

// METHOD 2
var dogName: String {
    return "Charlie"
}

// METHOD 3
let dogName = {
    return "Charlie"
}

// METHOD 4
var dogName: String = {
    return "Charlie"
}()

Obviously the method 3 declare a let and we known the difference; but why Swift allows the method 4?

What's the difference between these four methods?

I'm quite confusing in particular between method 2 and 4. In addition why the method 3 lose the final brackets compared to method 4?

Upvotes: 15

Views: 7525

Answers (4)

Thomas
Thomas

Reputation: 2063

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
}()

Upvotes: 13

vadian
vadian

Reputation: 285069

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 getter

    let 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 executed

    let 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 instance Foo() is initialized (to be proved by the print 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 as lazy

    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.

Upvotes: 3

user7014451
user7014451

Reputation:

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.

Upvotes: 2

Maroš Beťko
Maroš Beťko

Reputation: 2329

Method 1 is quite obvious.

In method 2 what you did is you defined a getter for given variable.

Type of dogName in method 3 is a () -> String and not String. What you initialized there is a named lambda.

In method 4 you initialize the variable with an anonymous(unnamed) lambda function that returns string.

Difference between 3 and 4 is that in 4th you call that function with () so you get String and in previous you don't so it's a function.

Upvotes: 1

Related Questions