如何在 Swift 中正确声明一个变量?

How properly declare a variable in Swift?

我发现这些在 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"
}()

显然方法 3 声明了一个 let,我们知道区别;但是为什么 Swift 允许方法 4?

这四种方法有什么区别?

我特别困惑方法 2 和方法 4。此外,为什么方法 3 与方法 4 相比失去了最后的括号?

方法一很明显。

在方法 2 中,您所做的是为给定变量定义一个 getter。

方法 3 中的 dogName 类型是 () -> String 而不是 String。你在那里初始化的是一个命名的 lambda。

在方法 4 中,您使用 returns 字符串的匿名(未命名)lambda 函数初始化变量。

3 和 4 之间的区别在于,在 4th 中你用 () 调用该函数所以你得到 String 而在之前你没有,所以它是一个函数。

我设置了一个快速测试,将所有内容重命名为 dogName1dogName2dogName3dogName4。然后我添加了代码以尝试将每个名称更改为 "Snoopy".

#2 和#3 没有构建,因为编译器知道它们都是只读的。 (#2,尽管被声明为 var,但始终设置为 return "Charlie"。

在注释掉这两行之后,我设置了两个断点 - 在初始化后打开,一个在尝试更新后设置。

最后我尝试对每一个都做 print

断点 #1:#1 和 #4 设置为 "Charlie",#2 不存在(因为它未初始化)和 #3显示为已初始化但没有值(因为尚未调用它。是的,最后的 () 初始化内存中的某些内容。

断点 #2: #1 和 #4 更新为 "Snoopy".

print的结果:#1 和#4 是 "Snoopy",#2 是 "Charlie",#3 是“(函数)".

结论:#1和#4没有区别。每个都被声明为 var 并且默认值为 "Charlie"。 #2,由于 let 是只读的,并且将始终 return "Charlie"。 #3?它会创建一个实例,如果您尝试更改它,它不会构建 - 但我不知道如何使用它。

如果有人对#3 有更多补充,我会更新这个答案。

方法 1 是 String 的标准变量声明。它有一个 setter 和一个 getter

var dogName: String = "Charlie"

print(dogName) -> "Charlie"
dogName = "Rex" // Valid

方法 2 是字符串类型的计算 属性,并且是只读的

var dogName: String {
    return "Charlie"
}

print(dogName) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only

方法 3 是 () -> String 类型的只读 属性,因此基本上是一个 lambda 函数。

let dogName = {
    return "Charlie"
}

print(dogName) -> "(Function)"
print(dogName()) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only

方法 4 是一个闭包,将在初始化包含对象时执行。因为它是 var 你可以用另一个值替换它

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

print(dogName) -> "Charlie"
dogName = "Rex" // Valid

话虽这么说,因为方法4是一个闭包,你可以在里面执行其他命令。这是一个示例,您可以在其中使用此构造来初始化 UILabel:

var dogNameLabel: UILabel = {
    let label = UILabel(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
    label.text = "Charlie"
    return label
}()

为了理解差异,让我们使用一个更具描述性的例子

这是一个classFoo

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

}

现在让我们创建一个实例

let foo = Foo()
  • 方法一是存储属性className与setter和getter

    let name = foo.className
    foo.className = "Bar"
    print(foo.className) // "Bar"
    
  • 方法 2 是计算出来的 属性 dogName1 只有一个 getter。它可用于动态计算值。

    print(foo.dogName1) // "Charlie Bar"
    
  • 方法三() -> String类型的闭包dogName2。可以赋值给一个变量,稍后执行

    let dogName = foo.dogName2 // assigns the closure but does not return the string.
    print(dogName()) // "My name is Charlie"
    
  • 方法4是变量dogName3当一个实例once立即执行它的关闭[= =23=]被初始化(由print行证明)

    print(foo.dogName3) // "My name is Charlie" but does not execute the closure and print `string` again 
    
  • 甚至还有一个方法5:如果你声明dogName3lazy

    lazy var dogName3 : String = {
    

    直到第一次访问变量时才执行闭包。优点是你甚至可以在闭包中使用 self 这在 方法 4.

  • 中是不可能的