在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())
在我看来,选择只读计算属性的最明显原因是:
description
是属性类的属性,而不是它执行的操作。显然,上面的例子过于简单,但还有其他充分理由选择其中一个吗?例如,是否有一些功能或属性的功能可以指导您决定使用哪种功能?
N.B。乍一看,这似乎是一个非常常见的OOP问题,但我很想知道任何特定于Swift的功能,这些功能可以指导使用这种语言时的最佳实践。
答案 0 :(得分:51)
在我看来,这主要是风格问题:我非常喜欢将属性用于:属性;意味着您可以获得和/或设置的简单值。我在实际工作时使用函数(或方法)。也许必须从磁盘或数据库中计算或读取某些东西:在这种情况下,我使用一个函数,即使只返回一个简单的值。这样我就可以很容易地看出一个电话是便宜的(属性)还是可能是昂贵的(功能)。
当Apple发布一些Swift编码约定时,我们可能会更加清晰。
答案 1 :(得分:12)
好吧,你可以申请Kotlin的建议https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties。
在某些情况下,没有参数的函数可以互换 具有只读属性。虽然语义相似,但有 关于什么时候喜欢彼此的一些风格约定。
在基础算法时首选属性而不是函数:
- 不会抛出
- 复杂度很便宜(或计算) 在第一次运行)
- 通过调用返回相同的结果
答案 2 :(得分:11)
虽然计算属性与方法的问题一般是困难和主观的,但目前在Swift的案例中有一个重要的论点,即偏好属性的方法。你可以使用Swift中的方法作为纯函数,这对于属性来说是不正确的(从Swift 2.0 beta开始)。这使得方法更加强大和有用,因为它们可以参与功能组合。
func fflat<A, R>(f: (A) -> () -> (R)) -> (A) -> (R) {
return { f($0)() }
}
func fnot<A>(f: (A) -> Bool) -> (A) -> (Bool) {
return { !f($0) }
}
extension String {
func isEmptyAsFunc() -> Bool {
return isEmpty
}
}
let strings = ["Hello", "", "world"]
strings.filter(fnot(fflat(String.isEmptyAsFunc)))
答案 3 :(得分:7)
由于运行时相同,因此该问题也适用于Objective-C。我说,你得到的属性
readwrite
didSet
进行更改通知的能力至于特定于Swift的东西,我唯一的例子是你可以使用@lazy
作为属性。
答案 4 :(得分:7)
有区别: 如果使用属性,则最终可以覆盖它并使其在子类中进行读/写。
答案 5 :(得分:4)
在只读的情况下,计算属性不应被视为在语义上等同于方法,即使它们的行为相同,因为删除func
声明会模糊数量之间的区别包含实例的 state 和仅仅是函数的数量的数量。您可以在通话网站上保存输入()
,但可能会在代码中失去清晰度。
作为一个简单的例子,请考虑以下矢量类型:
struct Vector {
let x, y: Double
func length() -> Double {
return sqrt(x*x + y*y)
}
}
通过将长度声明为方法,很明显它是状态的函数,仅取决于x
和y
。
另一方面,如果您要将length
表示为计算属性
struct VectorWithLengthAsProperty {
let x, y: Double
var length: Double {
return sqrt(x*x + y*y)
}
}
然后当您在IDE中对VectorWithLengthAsProperty
的实例进行dot-tab-complete时,看起来好像x
,y
,length
是平等的基础,这在概念上是不正确的。
答案 6 :(得分:2)
在某些情况下,您更喜欢计算属性而不是普通函数。如:返回一个人的全名。您已经知道名字和姓氏。所以fullName
属性实际上是属性而不是函数。在这种情况下,它是计算属性(因为你不能设置全名,你可以使用firstname和lastname提取它)
class Person{
let firstName: String
let lastName: String
init(firstName: String, lastName: String){
self.firstName = firstName
self.lastName = lastName
}
var fullName :String{
return firstName+" "+lastName
}
}
let william = Person(firstName: "William", lastName: "Kinaan")
william.fullName //William Kinaan
答案 7 :(得分:1)
从语义上讲,计算属性应该与对象的内在状态紧密耦合 - 如果其他属性没有改变,那么在不同时间查询计算属性应该给出相同的输出(通过==或===进行比较) ) - 类似于在该对象上调用纯函数。
另一方面,方法开箱即用,假设我们可能不会总是得到相同的结果,因为Swift没有办法将函数标记为纯。此外,OOP中的方法被视为操作,这意味着执行它们可能会导致副作用。如果该方法没有副作用,则可以安全地将其转换为计算属性。
请注意,上述两个语句纯粹来自语义角度,因为计算属性可能会产生我们不期望的副作用,而且方法也是纯粹的。
答案 8 :(得分:1)
从性能角度看,似乎没有什么区别。如您在基准测试结果中所见。
main.swift
代码段:
import Foundation
class MyClass {
var prop: Int {
return 88
}
func foo() -> Int {
return 88
}
}
func test(times: u_long) {
func testProp(times: u_long) -> TimeInterval {
let myClass = MyClass()
let starting = Date()
for _ in 0...times {
_ = myClass.prop
}
let ending = Date()
return ending.timeIntervalSince(starting)
}
func testFunc(times: u_long) -> TimeInterval {
let myClass = MyClass()
let starting = Date()
for _ in 0...times {
_ = myClass.prop
}
let ending = Date()
return ending.timeIntervalSince(starting)
}
print("prop: \(testProp(times: times))")
print("func: \(testFunc(times: times))")
}
test(times: 100000)
test(times: 1000000)
test(times: 10000000)
test(times: 100000000)
输出:
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
在图表中
答案 9 :(得分:0)
历史上描述是NSObject上的一个属性,很多人都希望它在Swift中继续相同。在它之后添加parens只会增加混乱。
编辑: 在愤怒的downvoting之后我必须澄清一些东西 - 如果它是通过点语法访问的,它可以被认为是属性。引擎盖下的内容并不重要。您无法使用点语法访问常用方法。
此外,调用此属性不需要额外的parens,如Swift的情况,这可能会导致混淆。