web-dev-qa-db-ja.com

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

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固有の機能について知りたいです。

95
Stuart

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

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

51

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

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

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

  • 投げない
  • 複雑さの計算は安価です(または、最初の実行時にキャッシュされます)
  • 呼び出しに対して同じ結果を返します
12
onmyway133

一般的に、計算されたプロパティ対メソッドの問題は難しく主観的ですが、現在、プロパティよりもメソッドを優先するというSwiftのケースには1つの重要な議論があります。 Swift=のプロパティはtrueではない純粋な関数としてメソッドを使用できます(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)))
11
Max O

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

7
Analog File

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

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

Swift固有の何かについて、私が持っている唯一の例は、@lazyプロパティ。

7
ilya n.

読み取り専用の場合、func宣言を削除すると、構成する量の区別が曖昧になるため、計算されたプロパティはnotメソッドと意味的に同等であると見なされます。インスタンスのstateおよび状態の単なるfunctionsである数量。入力を保存()呼び出しサイトで。ただし、コードの明瞭さが失われる危険があります。

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

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

VectorWithLengthAsPropertyのインスタンスのIDE)でドットタブ補完すると、xylengthは等しい基盤上のプロパティであり、概念的には正しくありません。

4
egnha

通常の機能よりも計算されたプロパティを好む状況があります。など:個人の氏名を返す。あなたはすでに名と姓を知っています。したがって、実際には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
2
William Kinaan

パフォーマンスの観点からは、違いはないようです。ベンチマーク結果でわかるように。

要旨

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

チャート内:

benchmark

1
Mint

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

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

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

1
Cristik

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

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

また、Swiftの場合のように、このプロパティを呼び出すのに余分な括弧は必要ありませんでした。

0
Dvole