繁体   English   中英

如何正确地在 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 相比失去了最后的括号?

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

var dogName: String = "Charlie"

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

方法二是String类型的计算属性,只读

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

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

这是一个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
    }()

}

现在让我们创建一个实例

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

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

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

    let dogName = foo.dogName2 // assigns the closure but does not return the string. print(dogName()) // "My name is Charlie"
  • 方法 4是变量dogName3 ,它在实例Foo()初始化时立即执行一次闭包(由print行证明)

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

     lazy var dogName3: String = {

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

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

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

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

最后我尝试print每一个。

断点 #1: #1 和 #4 设置为“Charlie”,#2 不存在(因为它未初始化)并且 #3 显示为已初始化但没有值(因为尚未调用。和是的,末尾的()初始化了内存中的一些东西。

断点 #2: #1 和 #4 已更新为“史努比”。

print结果: #1 和#4 是“Snoopy”,#2 是“Charlie”,#3 是“(Function)”。

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

如果有人要补充关于#3 的更多信息,我将更新此答案。

方法 1 非常明显。

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM