【问题标题】:How properly declare a variable in Swift?如何在 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 相比失去了最后的括号?

【问题讨论】:

    标签: swift swift3 var variable-declaration let


    【解决方案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 你完全正确,我相应地更新了答案。谢谢!
    • 计算属性并不总是只读的,顺便说一句。您可以在计算属性上定义 getset 以使其可读写。然而,当如上所示声明时,它们隐含地只有一个 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"
      
    • 方法3dogName2类型() -> 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】:

      我设置了一个快速测试,将所有内容重命名为 dogName1dogName2dogName3dogName4。然后我添加了代码以尝试将每个名称更改为“史努比”。

      #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,而在之前您没有,所以它是一个函数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-05-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-04-19
          • 2020-02-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多