为什么在Swift协议中不能使用let?

89

我对Swift协议中使用var{ get set }关键字存在疑惑。

根据苹果文档

如果一个协议要求属性同时具有可读性和可写性,那么该属性要求不能由常量存储属性或只读计算属性实现。如果协议仅要求属性具有可读性,则可以使用任何类型的属性来满足需求,如果这对自己的代码有用,则属性也可以是可写的。

属性需求总是声明为变量属性,前缀为var关键字。可读可写属性在其类型声明后加上{ get set },可读属性则在其类型声明后加上{ get }。

我不明白为什么我不能使用let。带有get的协议中的var不就是一个let吗?

就像这样:

protocol someProtocol 
{
   var someProperty: String { get }
}

这不仅仅是公平的问题:

protocol someProtocol 
{
   let someProperty: String
}

我漏掉了什么?


12
可获取的属性仍然可以更改值-协议实现者始终可以返回不同的值,协议调用者只是无法设置它们。另一方面,let 定义了一个常量,它只能被设置一次并且不能有不同的值。 - pvg
2
@pvg,为什么你没有把这个发表为答案呢? - 0x416e746f6e
是的,这里是正确答案。 - sunshinejr
7
回答简单问题时,往往会变成谁打字最长、最难理解的比赛。对于小事情,直接评论更容易。同时也有点懒。 - pvg
@pvg,我明白你的意思。一个人应该高兴地做正确的事情。 - 0x416e746f6e
4个回答

78

"在只有get的协议中定义的变量不就是let吗?" 不是。 let 表示常量,但在此处并非如此。 请考虑以下内容:

protocol SomeProtocol {
    var someProperty: String { get }
}

class SomeClass : SomeProtocol {

    var someProperty: String = ""

    func cla () {
        someProperty = "asd"
    }
}

let someInstance = SomeClass()

print(someInstance.someProperty) // outputs ""
someInstance.cla()
print(someInstance.someProperty) // outputs "asd"

协议规定了符合该协议的类向外界展示什么 - 一个名为someProperty,类型为String的属性,你至少可以获得它。

如果协议规定了{ get },那么你的类可以选择通过let someProperty: String = ""来符合协议,但也可以选择使用上述代码来符合协议。而如果协议规定了{ get set },则你在实现中不能使用let,而必须将其设为可设置。

协议无法定义一个值必须是常量 - 它也不应该这样做,这是由实现它的类/结构体来处理(或决定)的实现细节。


回头看,这是如此简单和明显的,协议只允许函数(然后是属性),但我总是更喜欢询问并解决任何疑问;现在使用 { get set } 就像以往一样清晰明了,再次感谢! - Massimo Polimeni
3
一个协议无法定义值必须是常量,它也不应该这样做。为什么不呢?为什么苹果公司没有提供更多细化的选项来定义let和var属性?或者你是说苹果公司本可以选择我所说的方式,而他们选择在实现层面上提供灵活性? - mfaani

54

区别在于

protocol MyProtocol {
    let someProperty: String
}

这是毫无意义的 —— 协议不应该规定someProperty的定义/存储方式,只需要确保它作为一个属性是可用的。它可以是计算属性或存储属性,但这取决于实现者,而不是协议本身。

以及

protocol MyProtocol {
    var someProperty: String { get }  // abstract interface
}

struct MyStruct: MyProtocol {
    let someProperty: String  // concrete implementation: stored property
}

struct OtherStruct: MyProtocol {
    let i: Int
    var someProperty: String { return "\(i)" }  // concrete implementation: computed property
}

这是完全允许的!


5
一个协议不应该规定如何定义/存储 someProperty。但是,协议是否定义了变量名称/类型/必须可设置等呢?为什么苹果没有提供更多细节并允许定义 let 和 var 属性? - mfaani

4

我认为协议可以要求结构体具备某些东西,但不能限制结构体或对象的功能。这不应该阻止您在代码中做您想做的事情,例如在协议中使用var,并在实现中使用let是可以接受的。

protocol MyProtocol {
    var trythis: Int { get }
}

struct MyStructure: MyProtocol {
    let trythis: Int
}

1
使用let声明的属性在底层被视为只读。因此,协议可以通过将其设置为只读来要求属性为常量。可以使用一些Objc运行时函数property_getAttributes来验证这个推论。
protocol SomeProtocol {
    var someTypeProperty: Int { get }
}

struct Foo: SomeProtocol {
    let someTypeProperty: Int
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接