[英]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中是不可能的。
我设置了一个快速测试,将所有内容重命名为dogName1
、 dogName2
、 dogName3
和dogName4
。 然后我添加代码以尝试将每个名称更改为“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.