如何在 Swift 中正确声明变量?
Posted
技术标签:
【中文标题】如何在 Swift 中正确声明变量?【英文标题】:How properly declare a variable in Swift? 【发布时间】:2017-12-16 23:43:47 【问题描述】:我发现这些在 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】:方法 1 是字符串的标准变量声明。它有一个 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是()->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
()
【讨论】:
好吧,您对方法 4 的看法并不完全正确。闭包将在对象初始化时执行,在该对象中声明属性,而不仅仅是在访问它时。因此,它就像从该闭包的结果分配的默认属性值。但你是对的,你以后可以用其他任何东西替换它。 @OlegDanu 你完全正确,我相应地更新了答案。谢谢! 计算属性并不总是只读的,顺便说一句。您可以在计算属性上定义get
和 set
以使其可读写。然而,当如上所示声明时,它们隐含地只有一个 getter,这实际上使它们成为只读的。见developer.apple.com/library/content/documentation/Swift/…
@ItaiFerber 是的,您是对的,计算属性并不总是只读的,感谢您指出这一点。我删除了 Method2 描述的错误部分。
@Sulthan 这不是真的。方法 3 是一个 lambda,您需要调用它才能获取该值。您可以通过let x = return 5 ; print(type(of: x))
确认这一点【参考方案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()
方法一是存储属性className
,带有setter和getter
let name = foo.className
foo.className = "Bar"
print(foo.className) // "Bar"
方法 2 是计算属性 dogName1
仅带有一个 getter。它可用于动态计算值。
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】:我设置了一个快速测试,将所有内容重命名为 dogName1
、dogName2
、dogName3
和 dogName4
。然后我添加了代码以尝试将每个名称更改为“史努比”。
#2 和#3 没有构建,因为编译器知道它们都是只读的。 (#2,尽管被声明为var
,但设置为始终返回“Charlie”。
在注释掉这两行之后,我设置了两个断点——在初始化之后,一个在尝试更新之后。
最后我尝试为每个人做一个print
。
断点 #1: #1 和 #4 设置为“Charlie”,#2 不存在(因为它没有初始化),而 #3 显示为已初始化但没有值(因为它还没有被调用。是的,最后的 ()
初始化了内存中的一些东西。
断点 #2: #1 和 #4 已更新为“史努比”。
print
的结果:#1 和#4 是“Snoopy”,#2 是“Charlie”,#3 是“(函数)”。
结论:#1 和#4 没有区别。每个都被声明为var
,并且默认为“Charlie”。 #2,由于let
是只读的,并且将始终返回“Charlie”。 #3?如果您尝试更改它,它会创建一个实例并且不会构建 - 但我不知道如何使用它。
如果有人对#3 有更多补充,我会更新这个答案。
【讨论】:
【参考方案4】:方法一很明显。
在方法 2 中,您所做的是为给定变量定义了一个 getter。
方法 3 中的 dogName 类型是 () -> String
而不是 String
。你在那里初始化的是一个命名的 lambda。
在方法 4 中,您使用返回字符串的匿名(未命名)lambda 函数初始化变量。
3 和 4 之间的区别在于,在第 4 次中,您使用 () 调用该函数,因此您获得 String,而在之前您没有,所以它是一个函数。
【讨论】:
以上是关于如何在 Swift 中正确声明变量?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 swift 的扩展中声明 NSLayoutConstraint 类型的变量
Swift Extension 计算变量在协议中声明之前无法正确读取
如何检查在 swift 中声明的 UnsafeMutablePointer<ObjCBool> 类型的变量的值? [复制]