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

如何正确地在Swift中声明变量?,swift,swift3,var,variable-declaration,let,Swift,Swift3,Var,Variable Declaration,Let,我发现在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中声明变量的这些不同方法非常有趣:

// 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非常明显

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

方法3中的狗名类型是
()->字符串
,而不是
字符串
。你初始化的是一个名为lambda的

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


3和4之间的区别在于,在第4节中,你用()调用该函数,这样你就得到了字符串,而在上一节中,你不这样做,所以它是一个函数。

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

let dogName = {
    return "Charlie"
}

print(dogName) -> "(Function)"
print(dogName()) -> "Charlie"
dogName = "Rex" // Invalid as property is read-only
#2和#3没有生成,因为编译器知道它们都是只读的。(#2,尽管被声明为
var
,但设置为始终返回“Charlie”

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

最后,我试着把每一张都打印出来

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

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

打印结果:#1和#4为“史努比”,#2为“查理”,而#3为“功能”

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


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

方法1是字符串的标准变量声明。它有一个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
}()

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

这是一个类
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是类型为
    ()->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
    行证明)

  • 甚至还有一个方法5:如果您将
    dogName3
    声明为
    lazy

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


您对方法4的看法并不完全正确。闭包将在对象初始化时执行,在对象初始化时声明属性,而不仅仅是在访问属性时执行。因此,它就像是从闭包结果中指定的默认属性值。但您是对的,以后可以用任何其他内容替换它。@OlegDanu是完全正确的,我相应地更新了答案。谢谢!计算属性并不总是只读的,顺便说一句。您可以在计算属性上定义
get
set
,使其可读写。但是,当如上所示声明时,它们隐式地只有一个getter,这实际上使它们是只读的。请参阅@ITAFERBER Yes您是对的,一个计算属性并不总是只读的,谢谢您指出这一点。我删除了Method2描述中的错误部分。@Sulthan这不是真的。Method 3是一个lambda,您需要调用它来获取值。您可以用
let x={return 5};print(type(of:x))