Disons que j'ai le protocole suivant:
protocol Identifiable {
var id: Int {get}
var name: String {get}
}
Et que j'ai les structs suivants:
struct A: Identifiable {
var id: Int
var name: String
}
struct B: Identifiable {
var id: Int
var name: String
}
Comme vous pouvez le constater, je devais "me conformer" au protocole identifiable de la structure A et de la structure B. Mais imaginons si j'avais plus de structures devant se conformer à ce protocole ... Je ne veux pas copier/coller 'la conformité (var id: Int, nom de la variable: String)
Je crée donc un extension de protocole:
extension Identifiable {
var id: Int {
return 0
}
var name: String {
return "default"
}
}
Avec cette extension, je peux maintenant créer une structure conforme au protocole identifiable sans avoir à implémenter les deux propriétés:
struct C: Identifiable {
}
Maintenant, le problème est que je ne peux pas définir de valeur pour la propriété id ou la propriété name:
var c: C = C()
c.id = 12 // Cannot assign to property: 'id' is a get-only property
Cela est dû au fait que, dans le protocole identifiable, l’identifiant et le nom ne sont que getting. Maintenant, si je change les propriétés id et name en {get set} j'obtiens l'erreur suivante:
Le type 'C' n'est pas conforme au protocole 'Identifiable'
Cette erreur se produit parce que je n'ai pas implémenté de setter dans l'extension de protocole ... Je change donc l'extension de protocole:
extension Identifiable {
var id: Int {
get {
return 0
}
set {
}
}
var name: String {
get {
return "default"
}
set {
}
}
}
Maintenant, l'erreur disparaît, mais si je définis une nouvelle valeur sur id ou name, la valeur par défaut (getter) est obtenue. Bien sûr, le passeur est vide.
Ma question est la suivante: Quel morceau de code dois-je mettre à l'intérieur du séparateur? Parce que si j'ajoute self.id = newValue il se bloque (récursif).
Merci d'avance.
Il semble que vous souhaitiez ajouter un stored property
à un type via une extension de protocole. Cependant, cela n'est pas possible car avec les extensions, vous ne pouvez pas ajouter une propriété stockée.
Je peux vous montrer quelques alternatives.
Le moyen le plus simple (comme vous l’imaginez probablement déjà) consiste à utiliser des classes au lieu de struct.
class IdentifiableBase {
var id = 0
var name = "default"
}
class A: IdentifiableBase { }
let a = A()
a.name = "test"
print(a.name) // test
Inconvénients: dans ce cas, votre classe A doit hériter de
IdentifiableBase
et puisque dans Swift il n'y a pas d'héritage multiple, ce sera la seule classe dont A pourra hériter.
Cette technique est très populaire dans le développement de jeux
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 }
}
}
Maintenant, vous pouvez adapter votre type à Identifiable
simplement en écrivant
struct A: Identifiable {
var identifiableComponent = IdentifiableComponent()
}
Tester
var a = A()
a.identifiableComponent.name = "test"
print(a.identifiableComponent.name) // test
Les protocoles et les extensions de protocole sont très puissants, mais ils ont tendance à être plus utiles pour les propriétés et les fonctions en lecture seule.
pour ce que vous essayez d'accomplir (propriétés stockées avec une valeur par défaut), les classes et l'héritage pourraient en fait être la solution la plus élégante
quelque chose comme:
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)")
C'est pourquoi vous n'avez pas pu définir les propriétés.
La propriété devient une propriété calculée, ce qui signifie qu’elle n’a pas de variable de support telle que _x comme dans ObjC. Dans le code de solution ci-dessous, vous pouvez voir que xTimesTwo ne stocke rien, mais calcule simplement le résultat à partir de x.
Voir Documents officiels sur les propriétés calculées.
Les fonctionnalités que vous souhaitez peuvent également être des observateurs de propriété.
Les setters/getters sont différents de ceux d’Objective-C.
Ce dont vous avez besoin c'est:
var x:Int
var xTimesTwo:Int {
set {
x = newValue / 2
}
get {
return x * 2
}
}
Vous pouvez modifier d’autres propriétés dans le setter/getters, ce à quoi elles sont destinées
Vous pouvez utiliser Objective-C objets associés pour ajouter en gros un stored property
à un class
ou protocol
. Notez que les objets associés ne fonctionnent que pour les objets de classe.
import ObjectiveC.runtime
protocol Identifiable: class {
var id: Int { get set }
var name: String { get set }
}
var IdentifiableIdKey = "kIdentifiableIdKey"
var IdentifiableNameKey = "kIdentifiableNameKey"
extension Identifiable {
var id: Int {
get {
return (objc_getAssociatedObject(self, &IdentifiableIdKey) as? Int) ?? 0
}
set {
objc_setAssociatedObject(self, &IdentifiableIdKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var name: String {
get {
return (objc_getAssociatedObject(self, &IdentifiableNameKey) as? String) ?? "default"
}
set {
objc_setAssociatedObject(self, &IdentifiableNameKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Maintenant, vous pouvez rendre votre class
conforme à Identifiable
en écrivant simplement
class A: Identifiable {
}
Tester
var a = A()
print(a.id) // 0
print(a.name) // default
a.id = 5
a.name = "changed"
print(a.id) // 5
print(a.name) // changed