let
不能用于只读计算属性的原因是,它用于说明该属性实际上的
值在被设置后永远不会改变,而不是该属性是只读的。正如
Apple文档所说(强调是我的):
必须使用 var
关键字声明计算属性,包括只读计算属性,因为它们的值不固定。 let
关键字仅用于常量属性,以指示它们的值在实例初始化后不能更改。
因此,您需要使用 var
来反映计算属性的值随时可能发生变化的事实,因为您在访问它时会即时创建它。虽然在您的代码中,这不可能发生,因为您的hello
和world
属性本身就是let
常量。但是,Swift无法推断出这一点,因此您仍然需要使用var
。
例如:
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
(这不会改变属性的可读性 - 因为您没有为其提供setter,所以它仍然是只读的)
但是在您的情况下,您可能想考虑使用延迟属性,因为您的hello
和world
属性是常量。延迟属性在首次访问时创建,并保留其值的余生 - 这意味着每次访问时都不必继续将两个常量连接在一起。
例如:
class Test {
let hello = "hello"
let world = "world"
lazy var phrase: String = {
return self.hello + self.world
}()
}
另一个
let
属性的特性是,在初始化之前必须知道它们的值。由于懒加载属性的值在此之前可能不确定,您还需要将其定义为
var
。
如果您仍然坚持想要一个
let
属性,那么我认为你有两个选择。
第一个选择是最简洁的(尽管你已经说过你不想这样做)- 你可以在初始化器中分配你的
phrase
属性。只要在调用
super.init
之前完成这个步骤,就不必处理可选项。例如:
class Test {
let hello = "hello"
let world = "world"
let phrase: String
init() {
phrase = hello+world
}
}
您无法在内联中完成此操作,因为该范围中的
self
是指静态类,而不是类实例。因此,您无法访问实例成员,必须使用
init()
或 lazy/calculated属性。
第二个选项相当hacky——您可以在类级别上镜像
hello
和
world
属性,从而可以在
phrase
声明中内联访问它们。例如:
class Test {
static let hello = "hello"
static let world = "world"
let hello:String = Test.hello
let world:String = Test.world
let phrase = hello+world
}
如果您实际上不需要将
hello
或
world
属性作为实例属性,则可以将它们设置为
static
,这将解决您的问题。
hello
和world
(都使用let
)的不可变性不能传递地应用于phrase
。从概念上讲,从不可变属性派生出来的内容本身也应该是不可变的。 - Alexanderphrase
与hello
和world
之间不应该有任何特别的区别。phrase
恰好是从计算属性返回的String
而不是String
字面量,这只是一个实现细节,与接口无关。 - Alexander