70

私が次のプロトコルを持っているとしましょう:

protocol Identifiable {
    var id: Int {get}
    var name: String {get}
}

そして、私は次の構造体を持っています:

struct A: Identifiable {
    var id: Int
    var name: String
}

struct B: Identifiable {
    var id: Int
    var name: String
}

ご覧のとおり、構造体 A と構造体 B の Identifiable プロトコルに「準拠」する必要がありました。しかし、このプロトコルに準拠する必要がある構造体がさらに N 個あると想像してみてください... 「コピー/貼り付け」したくありません' 適合性 (var id: Int、var name: String)

だから私はプロトコル拡張機能を作成します:

extension Identifiable {
    var id: Int {
        return 0
    }

    var name: String {
        return "default"
    }
}

この拡張により、両方のプロパティを実装しなくても、Identifiable プロトコルに準拠する構造体を作成できるようになりました。

struct C: Identifiable {

}

問題は、id プロパティまたは name プロパティに値を設定できないことです。

var c: C = C()
c.id = 12 // Cannot assign to property: 'id' is a get-only property

これは、Identifiable プロトコルでは、id と name のみが取得可能であるために発生します。id プロパティと name プロパティを{get set}に変更すると、次のエラーが発生します。

タイプ「C」はプロトコル「識別可能」に準拠していません

このエラーは、プロトコル拡張にセッターを実装していないために発生します...そのため、プロトコル拡張を変更します。

extension Identifiable {
    var id: Int {
        get {
            return 0
        }

        set {

        }
    }

    var name: String {
        get {
            return "default"
        }

        set {

        }
    }
}

エラーはなくなりましたが、新しい値を id または name に設定すると、デフォルト値 (getter) が取得されます。もちろん、セッターは空です。

私の質問は次 のとおりです。セッター内に配置する必要があるコードはどれですか? self.id = newValueを追加するとクラッシュするため (再帰的)。

前もって感謝します。

4

4 に答える 4

100

stored propertyプロトコル拡張を介して型にa を追加したいようです。ただし、拡張機能では保存されたプロパティを追加できないため、これは不可能です。

いくつかの代替案をお見せします。

サブクラス化 (オブジェクト指向プログラミング)

最も簡単な方法は (おそらく既にご想像のとおり)、構造体の代わりにクラスを使用することです。

class IdentifiableBase {
    var id = 0
    var name = "default"
}

class A: IdentifiableBase { }

let a = A()
a.name  = "test"
print(a.name) // test

短所: この場合、A クラスは継承する必要がIdentifiableBaseあります。Swift では多重継承がないため、A が継承できる唯一のクラスになります。

コンポーネント (プロトコル指向プログラミング)

この手法は、ゲーム開発で非常に一般的です

struct IdentifiableComponent {
    var id = 0
    var name = "default"
}

protocol HasIdentifiableComponent {
    var identifiableComponent: IdentifiableComponent { get set }
}

protocol Identifiable: HasIdentifiableComponent { }

extension Identifiable {
    var id: Int {
        get { return identifiableComponent.id }
        set { identifiableComponent.id = newValue }
    }
    var name: String {
        get { return identifiableComponent.name }
        set { identifiableComponent.name = newValue }
    }
}

Identifiableこれで、タイプを単純な記述に準拠させることができます

struct A: Identifiable {
    var identifiableComponent = IdentifiableComponent()
}

テスト

var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
于 2016-08-11T01:12:19.810 に答える
2

プロトコルとプロトコル拡張は非常に強力ですが、読み取り専用のプロパティと関数に最も役立つ傾向があります。

あなたが達成しようとしているもの(デフォルト値を持つ保存されたプロパティ)については、クラスと継承が実際にはより洗練された解決策かもしれません

何かのようなもの:

class Identifiable {
    var id: Int = 0
    var name: String = "default"
}

class A:Identifiable {
}

class B:Identifiable {
}

let a = A()

print("\(a.id) \(a.name)")

a.id = 42
a.name = "foo"

print("\(a.id) \(a.name)")
于 2016-08-11T01:07:24.883 に答える
0

これが、プロパティを設定できなかった理由です。

プロパティは計算されたプロパティになります。これは、ObjC の場合のように _x などのバッキング変数がないことを意味します。以下のソリューション コードでは、xTimesTwo が何も格納せず、単に x からの結果を計算していることがわかります。

計算されたプロパティに関する公式ドキュメントを参照してください。

必要な機能は、プロパティ オブザーバーである場合もあります。

セッター/ゲッターは、Objective-C とは異なります。

必要なものは次のとおりです。

var x:Int

var xTimesTwo:Int {
    set {
       x = newValue / 2
    }
    get {
        return x * 2
    }
}

セッター/ゲッター内の他のプロパティを変更できます。これは、それらが意図されているものです

于 2016-08-11T01:14:28.627 に答える