web-dev-qa-db-ja.com

Swiftプロトコルは設定のみ可能?

なぜ私はエラーなしでこれを行うことができます:

var testDto = ModelDto(modelId: 1)
testDto.objectId = 2

これを定義しながら:

protocol DataTransferObject {
    var objectType: DtoType { get }
    var parentObjectId: Int { get set }
    var objectId: Int { get }
    var objectName: String { get set }
}

struct ModelDto: DataTransferObject {
    var objectType: DtoType
    var parentObjectId: Int
    var objectId: Int
    var objectName: String

    init(modelId: Int) {
        self.objectType = DtoType.Model
        self.objectId = modelId
        self.parentObjectId = -1
        self.objectName = String()
    }
}

私のプロトコルの定義(ゲッター、セッターの定義)がほとんど無視される場合、とにかくそれらを使用する必要がありますか?

33
Artur Pajonk

公式ドキュメント

ゲッターとセッターの要件は、さまざまな方法で適合型によって満たされます。プロパティ宣言にgetキーワードとsetキーワードの両方が含まれる場合、準拠する型は、格納された変数プロパティ、または読み取りと書き込みの両方が可能な計算プロパティ(つまり、getterとsetterの両方を実装するプロパティ)で実装できます。ただし、そのプロパティ宣言は、定数プロパティまたは読み取り専用の計算プロパティとして実装することはできません。 プロパティ宣言にgetキーワードのみが含まれる場合、任意の種類のプロパティとして実装できます。

24
Ankit Goel

Appleは "Swift Programming Language(Swift 3)" :で述べています

プロトコルが取得可能なプロパティのみを必要とする場合、要件はあらゆる種類のプロパティで満たすことができ、これが独自のコードに役立つ場合、プロパティも設定可能であることが有効です。

このため、次の5つのPlaygroundコードスニペットはすべて有効です。

例#1:定数プロパティ

_protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    let fullName: String
}

let scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
_

例#2:可変プロパティ

_protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    var fullName: String        
}    

var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"
_

例#3:計算されたプロパティ(取得のみ)

_protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        return name
    }
}

let scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"
_

例#4:計算されたプロパティ(取得および設定)

_protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        get {
            return name
        }
        set {
            name = newValue
        }
    }
}

var scrooge = Duck(name: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.fullName = "Scrooge H. McDuck"
print(scrooge.fullName) // returns "Scrooge H. McDuck"
_

例5:private(set)変数プロパティ

_/* Duck.Swift located in Sources folder */

protocol FullyNamed {
    var fullName: String { get }
}

public struct Duck: FullyNamed {
    public private(set) var fullName: String

    public init(fullName: String) {
        self.fullName = fullName
    }

    public mutating func renameWith(fullName: String) {
        self.fullName = fullName
    }
}

/* Playground file */

var scrooge = Duck(fullName: "Scrooge McDuck")
print(scrooge.fullName) // returns "Scrooge McDuck"

scrooge.renameWith("Scrooge H. McDuck")
print(scrooge.fullName) // returns "Scrooge H. McDuck"
_

Appleは次のようにも述べています。

プロトコルがプロパティを取得および設定可能にする必要がある場合、そのプロパティ要件は、保存された定数プロパティまたは読み取り専用の計算プロパティでは満たすことができません。

このため、次の2つのPlaygroundコードスニペットはNOTが有効です。

例#1:定数プロパティ

_protocol FullyNamed {
    var fullName: String { get set }
}

struct Duck: FullyNamed {
    let fullName: String
}

let scrooge = Duck(fullName: "Scrooge McDuck")
// Error message: Type 'Duck' does not conform to protocol 'FullyNamed'
_

例#2:計算されたプロパティ(取得のみ)

_protocol FullyNamed {
    var fullName: String { get set }
}

struct Duck: FullyNamed {
    private var name: String
    var fullName: String {
        return name
    }
}

var scrooge = Duck(name: "Scrooge McDuck")
// Error message: Type 'Duck' does not conform to protocol 'FullyNamed'
_

例#3:計算されたプロパティ(取得のみ)

_protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    var fullName: String {return "Scrooge McDuck"}

    init(fullName: String) {
        self.fullName = fullName 
  // Error Message Cannot assign to Property: "FullName" is get only
    }
}
_
46
Imanou Petit

以下を考慮してください。

var testDto = ModelDto(modelId: 1)

ここの変数testDtoタイプはModelDtoであることが知られています。 ModelDtoは可変変数var objectId: Int。 objectIdは自由に変更できます。これは、取得可能なプロトコルインターフェイスではなく、ModelDtoインターフェイスを介してオブジェクトにアクセスするためです。

以下を試してください:

var testDto: DataTransferObject = ModelDto(modelId: 1)
testDto.objectId = 2 // compiler error

上記の例はコンパイルできません。 testDtoの型はDataTransferObjectであることがわかっているだけなので、基になる実装に設定可能なプロパティがあることはわかりません。プロトコルで宣言されているgettableプロパティについてのみ知っています。

要するに、あなたはModelDtoをget/set変数を持つように宣言したので、Swift did not let let get only変数を持つことは、プロトコルを介してオブジェクトを参照するか、objectIdModelDTOをlet変数に変更することに依存します。

編集:ModelDtoが設定可能な変数を持つことが許可されている理由に関する混乱に対処するため。これは、プロトコルで定義されている機能以外の機能をModelDtoに許可する方法と同じです。ゲッターとセッターは実際には単なる関数なので、ゲッターを必要とするプロトコルは実装がセッターを持つことを妨げるものではありません。 Objective Cでも同じことが可能です。プロトコルは記述的であり、制限的ではありません。

2
Patrick Goley

クラスで、objectIdという名前のストアドプロパティを作成します。プロトコルでは、プロパティにゲッターが必要であることを指定します。これが唯一の要件です。

期待どおりにコンピュータープロパティにする場合は、次のようにobjectIdを宣言する必要があります。

var objectId: Int{ return (someNumber) }

値を計算するクロージャーがない場合、デフォルトでは、保存されたプロパティです。

1
erdekhayser

私は一般的な意味で質問に答えています。

質問に取り組む前に、getsetの意味を理解する必要があります。

(Objective-Cの世界から来ている場合:) getreadOnlyを意味します。つまり、動物の足の数を知ることができます。設定することはできません。 getsetは、readWriteを意味します。つまり、動物の体重を設定/変更することもできますが、動物の体重を知ることができます。

次の例で。

_protocol Animal {
    var weight : Int { get set }
    var limbs : Int { get }
}
_

ゲッターのみがあり、セッターを非表示にしようとする場合(private (set)を使用して...)、エラーは発生しません...おそらくそれはあなたが望んでいたことであり、どのように行う必要があります!

意図したとおり:

_class Cat : Animal {
    private (set) var limbs: Int = 4 // This is what you intended, because you only have get requirements...and don't want any conforming type to be able to set it ie don't want others do catInstance.limbs = 22
    var weight: Int = 15

}

var smallCat = Cat()
smallCat.weight = 20 // Good!

// attempting to set it will create an error!!!
smallCat.limbs = 5 // Error: Cannot assign to property: 'limbs' setter is inaccessible
_

意図しないものと同様:

_class Panda : Animal {
    var limbs: Int = 4 // This is OK, but it kinda defeats the purpose of it being a get only
    var weight: Int = 200   
}

var littlPanda = Panda()

littlPanda.weight = 40 // Good
littlPanda.limbs = 30 // NO Error!!! Likely unintended
_

基本的に_{get}_を使用すると、まだいくつかのextraコンパイラが行うべき作業がありますdoes n'tあなたに... private (set)を追加する必要があります意図した動作を実現するため


プロパティにセッターがあり、セッターを非表示にしようとすると、実際にエラーが表示されます。

_class Dog : Animal {
    private (set) var limbs: Int = 4
    private (set) var weight: Int = 50  // Error: Setter for property 'weight' must be declared internal because it matches a requirement in internal protocol 'Animal'
}
_

あなたはセッターを提供することを約束したので、隠すことは許されません...

1
Honey

コードサンプルで見ている動作は、メンバーの非表示と呼ばれます。メンバの非表示は、新しいメンバが継承されたメンバと同じ名前またはシグネチャで宣言されたときにオブジェクト指向言語で発生するため、次のようになります:var objectId: Int構造体の実装では、objectIdという新しいメンバーを効果的に作成し、プロトコルから継承されたプロパティを非表示にします。

構造体とプロトコル間の契約を尊重するために、objectIdは次のように宣言できます。

  let objectId: Int = 1

または

var objectId: Int {
        get {
            return 1
        }
    }
0
omaraguirre