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

时间:2017-07-12 13:46:09

标签: 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允许方法4?

这四种方法的区别是什么?

我在方法2和方法4之间特别容易混淆。另外,为什么方法3与方法4相比失去了最后的括号?

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)

我设置了一个快速测试,将所有内容重命名为dogName2dogName3dogName4var。然后我添加了代码以尝试将每个名称更改为" 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个中你用()调用该函数,所以你得到了字符串,而在之前你不是这样,它是一个函数。