我发现在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声明了一个让我们知道差异;但为什么Swift允许方法4?
这四种方法的区别是什么?
我在方法2和方法4之间特别容易混淆。另外,为什么方法3与方法4相比失去了最后的括号?
答案 0 :(得分:8)
方法1是String的标准变量声明。它有一个setter和一个getter
var dogName: String = "Charlie"
print(dogName) -> "Charlie"
dogName = "Rex" // Valid
方法2是String类型的计算属性,是只读的
var dogName: String {
return "Charlie"
}
print(dogName) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
方法3是type() - >的只读属性。字符串,所以基本上是一个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
}()
答案 1 :(得分:2)
我设置了一个快速测试,将所有内容重命名为dogName2
,dogName3
,dogName4
和var
。然后我添加了代码以尝试将每个名称更改为" Snoopy"。
#2和#3没有构建,因为编译器知道它们都是只读的。 (#2,尽管被声明为print
,但设置为始终返回"查理"。
在对这两行进行评论之后,我设置了两个断点 - 初始化后打开,尝试更新后打开一个断点。
最后我尝试了每个()
。
断点#1:#1和#4设置为" Charlie",#2不在那里(因为它没有被初始化)和#3显示为已初始化但没有值(因为它尚未被调用。是的,最后的print
会在内存中初始化内容。
断点#2:#1和#4更新为"史努比"。
var
:#1和#4的结果是"史努比",#2是"查理" #3是"(功能)"。
结论:#1和#4之间没有区别。每个都声明为let
,默认为"查理"。由于let win = window.open(null, "_self");
win.close();
,#2是只读的,并且将始终返回" Charlie"。 #3?如果你试图改变它,它会创建一个实例并且不会构建 - 但我不知道如何使用它。
如果有人有关于#3的更多内容,我会更新此答案。
答案 2 :(得分:2)
要理解这些差异,请使用更具描述性的示例
这是一个班级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()
方法1 是带有setter和getter的存储属性className
let name = foo.className
foo.className = "Bar"
print(foo.className) // "Bar"
方法2 是仅使用getter的计算属性dogName1
。它可以用来动态计算值。
print(foo.dogName1) // "Charlie Bar"
方法3 是dogName2
类型的结束() -> String
。它可以分配给变量,然后执行
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 中是不可能的。
答案 3 :(得分:1)
方法1非常明显。
在方法2中,您所做的是为给定变量定义了一个getter。
方法3中dogName的类型是() -> String
而不是String
。你在那里初始化的是一个名为lambda。
在方法4中,使用返回字符串的匿名(未命名)lambda函数初始化变量。
3和4之间的区别在于,在第4个中你用()调用该函数,所以你得到了字符串,而在之前你不是这样,它是一个函数。