web-dev-qa-db-fra.com

Le protocole Swift est-il seulement configurable?

pourquoi puis-je le faire sans erreur:

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

alors que je définis ceci:

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

Si la définition dans mon protocole est la plupart du temps ignorée (définition de getter, setter), pourquoi devrais-je les utiliser quand même?

28
Artur Pajonk

Selon le documentation officielle

Les exigences relatives au getter et au setter peuvent être satisfaites par un type conforme de différentes manières. Si une déclaration de propriété inclut à la fois les mots-clés get et set, un type conforme peut l'implémenter avec une propriété de variable stockée ou une propriété calculée, lisible et inscriptible (c'est-à-dire qui implémente à la fois un getter et un setter). Cependant, cette déclaration de propriété ne peut pas être implémentée en tant que propriété constante ou en tant que propriété calculée en lecture seule. Si une déclaration de propriété n'inclut que le mot clé get, elle peut être implémentée comme tout type de propriété. 

21
Ankit Goel

Apple déclare dans le "Swift Programming Language (Swift 3)" :

Si le protocole requiert uniquement qu'une propriété soit getting, l'exigence peut être satisfaite par n'importe quel type de propriété. Cette propriété est également valide si elle est utile pour votre propre code.

Pour cette raison, les cinq extraits de code suivants de Playground sont tous valides:

Exemple n ° 1: propriété constante

protocol FullyNamed {
    var fullName: String { get }
}

struct Duck: FullyNamed {
    let fullName: String
}

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

Exemple n ° 2: propriété de variable

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"

Exemple # 3: propriété calculée (get seulement)

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"

Exemple # 4: propriété calculée (get et set)

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"

Exemple n ° 5: private(set) propriété de variable

/* 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 déclare également:

Si un protocole nécessite qu'une propriété soit paramétrable et définissable, cette propriété ne peut pas être remplie par une propriété stockée constante ou une propriété calculée en lecture seule.

Pour cette raison, les deux extraits de code de Playground suivants ARE NOT sont valides:

Exemple n ° 1: propriété constante

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'

Exemple # 2: propriété calculée (get seulement)

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'

Exemple # 3: propriété calculée (get seulement)

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
    }
}
40
Imanou Petit

Dans votre classe, vous créez une propriété stockée nommée objectId. Dans votre protocole, vous spécifiez que la propriété a besoin d'un getter - c'est sa seule exigence.

Si vous vouliez que ce soit une propriété d'ordinateur, comme vous vous y attendiez, vous devez déclarer objectId avec les éléments suivants:

var objectId: Int{ return (someNumber) }

Sans la fermeture pour calculer la valeur, il s'agit par défaut d'une propriété stockée.

1
erdekhayser

Je réponds à la question dans son sens générique.

Avant de répondre à la question, vous devez savoir ce que signifie get & set.

(Si vous venez d'un monde Objective-C :) get signifie readOnly , c'est-à-dire que je suis autorisé à connaître le nombre de pattes d'un animal. Je ne suis pas autorisé à le définir. get & set ensemble signifie readWrite c’est-à-dire que j’ai le droit de connaître le poids d’un animal tout en étant également capable de le définir/de le modifier.

Avec l'exemple suivant.

protocol Animal {
    var weight : Int { get set }
    var limbs : Int { get }
}

Si vous n'avez que le getter et que vous essayez de cacher le setter (en utilisant private (set) ..., alors vous n'obtiendrez pas d'erreur. C'est probablement ce que vous vouliez et comment il faut le faire!

Probablement ce que vous vouliez:

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

Probablement ce que vous n'aviez pas l'intention:

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

En gros, avec {get}, il reste encore du travail extra que le compilateur ne vous dit pas ... VOUS devez ajouter private (set) pour obtenir le comportement souhaité.


Si votre propriété a setter et que vous essayez de masquer le setter, vous verrez une erreur.

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

Vous n'êtes pas autorisé à vous cacher, car vous avez promis de fournir un passeur ...

1
Honey

Le comportement que vous observez sur votre exemple de code s'appelle masquage de membre. La dissimulation de membre se produit dans les langages orientés objet lorsqu'un nouveau membre est déclaré avec le même nom ou la même signature qu'un membre hérité. var objectId: Int dans votre implémentation de structure, vous créez effectivement un nouveau membre appelé objectId et masquez la propriété héritée du protocole.

Afin d'honorer le contrat entre votre structure et votre protocole, objectId pourrait être déclaré comme:

  let objectId: Int = 1

ou 

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