在Swift WWDC会话简介中,演示了一个只读属性 description
:
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheels"
}
}
let vehicle = Vehicle()
println(vehicle.description)
选择上述方法而不是使用方法有任何影响:
class Vehicle {
var numberOfWheels = 0
func description() -> String {
return "\(numberOfWheels) wheels"
}
}
let vehicle = Vehicle()
println(vehicle.description())
在我看来,你选择只读计算属性的最明显的原因是:
-
Semantics - 在这个例子中,
description
是一个类的属性,而不是它执行的动作 . -
Brevity/Clarity - 在获取值时无需使用空括号 .
显然,上面的例子过于简单,但还有其他充分理由选择其中一个吗?例如,是否有某些功能或属性的功能可以指导您决定使用哪些功能?
注:乍一看,这似乎是一个非常常见的OOP问题,但我很想知道任何特定于Swift的功能,这些功能可以指导使用这种语言时的最佳实践 .
10 回答
在我看来,这主要是风格问题:我非常喜欢使用 properties :意味着您可以获得和/或设置的简单值 . 我在实际工作时使用 functions (或方法) . 也许必须从磁盘或数据库中计算或读取某些东西:在这种情况下,我使用一个函数,即使只返回一个简单的值 . 这样我就可以很容易地看到一个电话是便宜的(属性)还是可能是昂贵的(函数) .
当Apple发布一些Swift编码约定时,我们可能会更加清晰 .
好吧,你可以申请Kotlin的建议https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties .
虽然计算属性与方法的问题一般是困难和主观的,但目前在Swift的案例中有一个重要的论点,即偏好属性的方法 . 你可以使用Swift中的方法作为纯函数,这对于属性来说是不正确的(从Swift 2.0 beta开始) . 这使得方法更加强大和有用,因为它们可以参与功能组合 .
有一点不同:如果使用属性,则最终可以覆盖它并使其在子类中进行读/写 .
由于运行时是相同的,因此该问题也适用于Objective-C . 我会说,你得到的属性
在子类中添加setter的可能性,使属性
readwrite
能够使用KVO /
didSet
进行更改通知更一般地说,您可以将属性传递给期望关键路径的方法,例如获取请求排序
至于特定于Swift的东西,我唯一的例子是你可以使用
@lazy
作为属性 .在只读的情况下,计算属性应该被认为在语义上等同于方法,即使它们的行为相同,因为删除
func
声明会模糊构成实例状态的数量与仅仅是函数的数量之间的区别 . 国家 . 您可以在呼叫站点保存输入()
,但可能会在代码中失去清晰度 .作为一个简单的例子,请考虑以下矢量类型:
通过将长度声明为方法,很明显它是状态的函数,它仅取决于
x
和y
.另一方面,如果您将
length
表示为计算属性然后当您在IDE中的
VectorWithLengthAsProperty
实例上进行dot-tab-complete时,看起来好像x
,y
,length
属于平等的属性,这在概念上是不正确的 .在某些情况下,您更喜欢计算属性而不是普通函数 . 如:返回一个人的全名 . 您已经知道名字和姓氏 . 所以
fullName
属性实际上是属性而不是函数 . 在这种情况下,它是计算属性(因为你不能设置全名,你可以使用firstname和lastname提取它)从性能的角度来看,似乎没有什么区别 . 正如你在基准测试中看到的那样结果 .
gist
main.swift
code snippet:输出:
prop: 0.0380070209503174 func: 0.0350250005722046 prop: 0.371925950050354 func: 0.363085985183716 prop: 3.4023300409317 func: 3.38373708724976 prop: 33.5842199325562 func: 34.8433820009232 Program ended with exit code: 0
在图表中:
从语义上讲,计算属性应该与对象的内在状态紧密耦合 - 如果其他属性没有改变,那么在不同时间查询计算属性应该给出相同的输出(通过==或===进行比较) - 类似在该对象上调用纯函数 .
另一方面,方法开箱即用,假设我们可能不会总是得到相同的结果,因为Swift没有办法将函数标记为纯函数 . 此外,OOP中的方法被视为操作,这意味着执行它们可能会导致副作用 . 如果该方法没有副作用,则可以安全地将其转换为计算属性 .
请注意,上述两个语句纯粹来自语义角度,因为计算属性可能会产生我们不期望的副作用,并且方法是纯粹的 .
历史上描述是NSObject上的一个属性,许多人都希望它在Swift中继续相同 . 在它之后添加parens只会增加混乱 .
编辑:在激烈的downvoting后,我必须澄清一些东西 - 如果它是通过点语法访问,它可以被视为属性 . 引擎盖下的内容并不重要 . 您无法使用点语法访问常用方法 .
此外,调用此属性不需要额外的parens,如Swift的情况,这可能会导致混淆 .