Swiftで計算された読み取り専用プロパティvs関数


97

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、クラスが実行するアクションではなく、クラスのプロパティであることが理にかなっています。
  • 簡潔/明確 -値を取得するときに空の括弧を使用する必要がなくなります。

明らかに上記の例は非常に単純ですが、他のものを選択する他の理由はありますか?たとえば、どちらを使用するかの決定を導く関数またはプロパティのいくつかの機能はありますか?


注:一見、これはOOPに関するよくある質問のように見えますが、この言語を使用する際のベストプラクティスを導くSwift固有の機能を知りたいと思っています。


1
204セッションを見る-「@propertyを使用しない場合」いくつかのヒントがあります
Kostiantyn Koval 14

4
待って、読み取り専用プロパティを実行してget {}?知らなかった、ありがとう!
Dan Rosenstark、2015年

WWDC14セッション204はこちら(ビデオとスライド)、developer.apple.com
videos / play / wwdc2014 /

回答:


52

それは主にスタイルの問題だと私には思えます。私はそのためにプロパティを使用することを強く好みます。取得または設定できる単純な値を意味します。実際の作業が行われているときに関数(またはメソッド)を使用します。おそらく、何かを計算するか、ディスクまたはデータベースから読み取る必要があります。この場合、単純な値のみが返される場合でも、関数を使用します。こうすることで、通話が安い(プロパティ)か、場合によっては高い(関数)かを簡単に確認できます。

AppleがいくつかのSwiftコーディング規約を公開すると、おそらくより明確になるでしょう。


12

そうですね、Kotlinのアドバイスhttps://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-propertiesを適用できます。

場合によっては、引数のない関数は読み取り専用プロパティと交換可能です。セマンティクスは似ていますが、どちらを優先するかについてはいくつかのスタイル上の規則があります。

基本となるアルゴリズムが次の場合、関数よりもプロパティを優先します。

  • 投げません
  • 複雑さを計算するのは簡単です(または最初の実行時に計算されます)
  • 呼び出しに対して同じ結果を返します

1
「has a O(1)」の提案はそのアドバイスに含まれなくなりました。
デビッドペティグリュー

Kotlinの変更を反映するように編集されました。
Carsten Hagemann

11

計算されたプロパティとメソッドの一般的な問題は困難で主観的ですが、現在、プロパティよりもメソッドを優先するというSwiftのケースには1つの重要な議論があります。プロパティには当てはまらない純粋な関数としてSwiftのメソッドを使用できます(Swift 2.0ベータ以降)。これにより、メソッドは機能合成に参加できるため、メソッドはより強力で便利になります。

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)))

1
strings.filter {!$(0).isEmpty}-同じ結果を返します。これは、Array.filter()に関するアップルのドキュメントのサンプルを変更したものです。そして、それは理解するのがはるかに簡単です。
poGUIst 2018

7

ランタイムは同じなので、この質問はObjective-Cにも当てはまります。私が言うには、あなたが得るプロパティで

  • サブクラスにセッターを追加してプロパティを作成する可能性 readwrite
  • didSet変更通知にKVO /を使用する機能
  • より一般的には、キーパスを期待するメソッドにプロパティを渡すことができます(例:フェッチ要求のソート)

Swiftに固有のものについては、私が持っている唯一の例@lazyは、プロパティに使用できることです。


7

違いがあります。プロパティを使用する場合、最終的にそれをオーバーライドして、サブクラスでプロパティを読み取り/書き込みにすることができます。


9
関数をオーバーライドすることもできます。または、セッターを追加して書き込み機能を提供します。
Johannes Fahrenkrug 2014年

基本クラスが名前を関数として定義したときに、セッターを追加したり、保存されたプロパティを定義したりできますか?確かに、それがプロパティを定義している場合はそれを実行できます(それがまさに私のポイントです)が、関数を定義している場合は実行できないと思います。
アナログファイル

Swiftにプライベートプロパティ(ここでstackoverflow.com/a/24012515/171933を参照)が設定されたら、サブクラスにセッター関数を追加して、そのプライベートプロパティを設定できます。getter関数が「name」と呼ばれる場合、setterは「setName」と呼ばれるため、名前の競合は発生しません。
Johannes Fahrenkrug 2014年

すでにそれを行うことができます(違いは、サポートに使用する格納されたプロパティがパブリックになることです)。しかしOPは、ベースでの読み取り専用プロパティまたは関数の宣言に違いがあるかどうか尋ねました。読み取り専用プロパティを宣言すると、派生クラスで読み取り/書き込み可能にすることができます。将来の派生クラスをまったく知らなくても、基本クラスに追加willSetdidSetて拡張機能を使用すると、オーバーライドされたプロパティの変更を検出できます。でも関数ではそのようなことはできないと思います。
アナログファイル

読み取り専用プロパティをオーバーライドしてセッターを追加するにはどうすればよいですか?ありがとう。「サブクラスプロパティのオーバーライドでゲッターとセッターの両方を提供することで、継承された読み取り専用プロパティを読み書き可能なプロパティとして提示できます」とドキュメントに表示されますが、セッターが書き込む変数は何ですか?
Dan Rosenstark、2015年

5

読み取り専用の場合には、計算されたプロパティはべきではない落下するので、それらは同じ動作場合でも、メソッドと意味的に同等と考えることがfunc宣言すること含む量との間の区別あいまい状態単にあるインスタンスのおよび量機能のを状態。()呼び出しサイトでのタイピングを節約できますが、コードの明瞭さが失われるリスクがあります。

簡単な例として、次のベクトルタイプを考えてみます。

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

方法として、長さを宣言することにより、それが唯一依存する状態、の機能だということは明らかだxy

一方、length計算されたプロパティとして表現する場合

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

あなたがのインスタンス上のIDEでタブ補完ドットときにVectorWithLengthAsProperty、それがあるかのように見えるxylength概念的には間違っている対等な立場、上の性質でした。


5
これは面白いですが、計算された読み取り専用のプロパティはどこの例与えることができます。この原理を以下の際に使用することを?たぶん私は間違っているかもしれませんが、あなたの議論はそれらが使用されるべきではないことを示唆しているようです。
Stuart

2

通常の関数よりも計算されたプロパティを好む状況があります。など:人のフルネームを返す。あなたはすでに名と姓を知っています。つまり、fullNameプロパティは関数ではなくプロパティです。この場合、それは計算されたプロパティです(フルネームを設定できないため、名と姓を使用して抽出できます)

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

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

チャート:

基準


2
Date()オペレーティングシステムによる自動更新の対象となるコンピューターのクロックを使用するため、ベンチマークには適していません。mach_absolute_timeより信頼性の高い結果が得られます。
Cristik

1

意味論的に言えば、計算されたプロパティはオブジェクトの固有の状態と密接に結合している必要があります。他のプロパティが変更されない場合、計算されたプロパティをさまざまなタイミングでクエリすると、同じ出力が得られます(==または===で比較可能)-同様そのオブジェクトで純粋な関数を呼び出すことに。

一方、Swiftには関数を純粋なものとしてマークする方法がないため、メソッドは常に同じ結果を得るとは限らないと想定して、すぐに使用できます。また、OOPのメソッドはアクションと見なされます。つまり、メソッドを実行すると副作用が発生する可能性があります。メソッドに副作用がない場合は、計算されたプロパティに安全に変換できます。

上記のステートメントはどちらも純粋にセマンティックの観点からのものであることに注意してください。計算されたプロパティに予期しない副作用があり、メソッドが純粋であることはよくあることです。


0

歴史的に説明はNSObjectのプロパティであり、Swiftでも同じことが続くと多くの人が予想しています。括弧を追加した後で混乱が生じるだけです。

編集:猛烈な投票の後、私は何かを明確にする必要があります-ドット構文を介してアクセスされる場合、それはプロパティと見なすことができます。何が隠されているかは関係ありません。ドット構文では通常のメソッドにアクセスできません。

さらに、Swiftの場合のように、このプロパティを呼び出すために余分な括弧を必要としなかったため、混乱が生じる可能性があります。


1
実際にはこれは正しくありません。これはプロトコルでdescription必要なメソッドであるNSObjectため、objective-Cではを使用して返され[myObject description]ます。とにかく、プロパティdescriptionは単なる不自然な例でした-私はカスタムプロパティ/関数に適用されるより一般的な答えを探しています。
Stuart

1
いくつかの説明をありがとう。あなたの推論は理解していますが、値を返すパラメーターなしのobj-cメソッドはプロパティと見なすことができるというあなたの声明に完全に同意するかどうかはまだわかりません。私は今のところ反対票を撤回しますが、この回答は質問で既に述べた「セマンティクス」の理由を説明していると思います。また、言語間の一貫性はここでも問題ではありません。
スチュアート
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.