102

In the Introduction to Swift WWDC session, a read-only property description is demonstrated:

class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheels"
    }
}

let vehicle = Vehicle()
println(vehicle.description)

Are there any implications to choosing the above approach over using a method instead:

class Vehicle {
    var numberOfWheels = 0
    func description() -> String {
        return "\(numberOfWheels) wheels"
    }
}

let vehicle = Vehicle()
println(vehicle.description())

It seems to me that the most obvious reasons you would choose a read-only computed property are:

  • Semantics - in this example it makes sense for description to be a property of the class, rather than an action it performs.
  • Brevity/Clarity - prevents the need to use empty parentheses when getting the value.

Clearly the above example is overly simple, but are there other good reasons to choose one over the other? For example, are there some features of functions or properties that would guide your decision of which to use?


N.B. At first glance this seems like quite a common OOP question, but I'm keen to know of any Swift-specific features that would guide best practice when using this language.

Stuart
  • 34,797
  • 19
  • 93
  • 135

10 Answers10

55

It seems to me that it's mostly a matter of style: I strongly prefer using properties for just that: properties; meaning simple values that you can get and/or set. I use functions (or methods) when actual work is being done. Maybe something has to be computed or read from disk or from a database: In this case I use a function, even when only a simple value is returned. That way I can easily see whether a call is cheap (properties) or possibly expensive (functions).

We will probably get more clarity when Apple publishes some Swift coding conventions.

Johannes Fahrenkrug
  • 38,500
  • 17
  • 113
  • 155
13

Well, you can apply Kotlin 's advices https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties.

In some cases functions with no arguments might be interchangeable with read-only properties. Although the semantics are similar, there are some stylistic conventions on when to prefer one to another.

Prefer a property over a function when the underlying algorithm:

  • does not throw
  • complexity is cheap to calculate (or caсhed on the first run)
  • returns the same result over invocations
Carsten Hagemann
  • 576
  • 5
  • 20
onmyway133
  • 38,911
  • 23
  • 231
  • 237
11

While a question of computed properties vs methods in general is hard and subjective, currently there is one important argument in the Swift's case for preferring methods over properties. You can use methods in Swift as pure functions which is not true for properties (as of Swift 2.0 beta). This makes methods much more powerful and useful since they can participate in functional composition.

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)))
Max O
  • 941
  • 8
  • 13
  • 1
    strings.filter {!$(0).isEmpty} - returns same result. It is modified sample from apple documentation on Array.filter(). And it is much more easy to understand. – poGUIst Jul 26 '18 at 19:06
7

Since the runtime is the same, this question applies to Objective-C as well. I'd say, with properties you get

  • a possibility of adding a setter in a subclass, making the property readwrite
  • an ability to use KVO/didSet for change notifications
  • more generally, you can pass property to methods that expect key paths, e.g. fetch request sorting

As for something specific to Swift, the only example I have is that you can use @lazy for a property.

ilya n.
  • 16,814
  • 14
  • 66
  • 89
7

There is a difference: If you use a property you can then eventually override it and make it read/write in a subclass.

Analog File
  • 5,149
  • 18
  • 23
  • 9
    You can also override functions, too. Or add a setter to provide writing ability. – Johannes Fahrenkrug Jun 05 '14 at 12:45
  • You can add a setter or define a stored property when the base class defined the name as a function? Surely you can do it if it defined a property (that's exactly my point), but I do not think you can do it if it defined a function. – Analog File Jun 05 '14 at 13:23
  • Once Swift has private properties (see here http://stackoverflow.com/a/24012515/171933), you could simply add a setter function to your subclass to set that private property. When your getter function is called "name", your setter would be called "setName", so no naming conflict. – Johannes Fahrenkrug Jun 05 '14 at 13:52
  • You can do it already (the difference is that the stored property you use for support will be public). But the OP asked if there is a difference between declaring a read only property or a function in the base. If you declare a read only property, you can then make it read-write in a derived class. An extension that adds `willSet` and `didSet` to the __base__ class, without knowing anything of future derived classes, can detect changes in the overridden property. But you cannot do anything like that with functions, I think. – Analog File Jun 05 '14 at 14:04
  • How can you override a read-only property to add a setter? Thanks. I see this in the docs, "You can present an inherited read-only property as a read-write property by providing both a getter and a setter in your subclass property override" but... what variable does the setter write to? – Dan Rosenstark Nov 02 '15 at 18:00
6

In the read-only case, a computed property should not be considered semantically equivalent to a method, even when they behave identically, because dropping the func declaration blurs the distinction between quantities that comprise the state of an instance and quantities that are merely functions of the state. You save typing () at the call site, but risk losing clarity in your code.

As a trivial example, consider the following vector type:

struct Vector {
    let x, y: Double
    func length() -> Double {
        return sqrt(x*x + y*y)
    }
}

By declaring the length as a method, it’s clear that it’s a function of the state, which depends only on x and y.

On the other hand, if you were to express length as a computed property

struct VectorWithLengthAsProperty {
    let x, y: Double
    var length: Double {
        return sqrt(x*x + y*y)
    }
}

then when you dot-tab-complete in your IDE on an instance of VectorWithLengthAsProperty, it would look as if x, y, length were properties on an equal footing, which is conceptually incorrect.

egnha
  • 969
  • 10
  • 20
  • 5
    This is interesting, but can you give an example of where a computed read-only property _would_ be used when following this principle? Maybe I'm wrong, but your argument seems to suggest that they should _never_ be used, since by definition a computed read-only property never comprises state. – Stuart Jan 10 '18 at 21:38
2

There are situations where you would prefer computed property over normal functions. Such as: returning the full name of an person. You already know the first name and the last name. So really the fullName property is a property not a function. In this case, it is computed property (because you can't set the full name, you can just extract it using the firstname and the 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
William Kinaan
  • 25,507
  • 20
  • 76
  • 115
1

Semantically speaking, computed properties should be tightly coupled with the intrinsic state of the object - if other properties don't change, then querying the computed property at different times should give the same output (comparable via == or ===) - similar to calling a pure function on that object.

Methods on the other hand come out of the box with the assumption that we might not always get the same results, because Swift doesn't have a way to mark functions as pure. Also, methods in OOP are considered actions, which means that executing them might result in side effects. If the method has no side effects, then it can safely be converted to a computed property.

Note that both of the above statements are purely from a semantic perspective, as it might well happen for computed properties to have side effects that we don't expect, and methods to be pure.

Cristik
  • 24,833
  • 18
  • 70
  • 97
1

From the performance perspective, there seems no difference. As you can see in the benchmark result.

gist

main.swift code snippet:

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)

Output:

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

In Chart:

benchmark

Benjamin Wen
  • 2,386
  • 1
  • 21
  • 38
  • 3
    `Date()` is not suitable for benchmarks as it uses the computer clock, which is subject to automatic updates by the operating system. `mach_absolute_time` would get more reliable results. – Cristik Jul 20 '18 at 03:47
0

Historically description is a property on NSObject and many would expect that it continues the same in Swift. Adding parens after it will only add confusion.

EDIT: After furious downvoting I have to clarify something - if it is accessed via dot syntax, it can be considered a property. It doesn't matter what's under the hood. You can't access usual methods with dot syntax.

Besides, calling this property did not require extra parens, like in the case of Swift, which may lead to confusion.

Dvole
  • 5,483
  • 8
  • 50
  • 86
  • 1
    Actually this is incorrect - `description` is a required _method_ on the `NSObject` protocol, and so in objective-C is returned using `[myObject description]`. Anyway, the property `description` was simply a contrived example - I'm looking for a more generic answer that applies to any custom property/function. – Stuart Jun 04 '14 at 11:23
  • 1
    Thanks for some clarification. I'm still not sure I completely agree with your statement that any parameterless obj-c method that returns a value can be considered a property, although I do understand your reasoning. I'll retract my down vote for now, but I think this answer is describing the 'semantics' reason already mentioned in the question, and cross-language consistency isn't really the issue here either. – Stuart Jun 04 '14 at 12:02