583 votes

Comment puis-je faire une référence de protocole faible en Swift "pur" (sans @objc) ?

Les références faibles ne semblent pas fonctionner en Swift, sauf si un protocole est déclaré comme @objc, ce que je ne veux pas dans une application Swift pure :-)

Ce code donne lieu à une erreur de compilation ('weak' ne peut pas être appliqué au type non classe 'MyClassDelegate') :

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Je dois préfixer le protocole avec @objc, puis cela fonctionne.

Question : Quelle est la méthode Swift "pure" pour réaliser un délégué faible ?

0 votes

1091voto

flainez Points 851

Mise à jour (Beta 5) : Xcode / Swift Beta 5 a changé la façon de déclarer l'exigence de classe pour un protocole. Au lieu d'utiliser @class_protocol vous devez maintenant déclarer le type du protocole comme étant class .

Avec la nouvelle syntaxe, l'exemple de code que j'ai posté avec la réponse originale serait le suivant :

protocol ProtocolNameDelegate: class {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

L'idée est la même, mais la syntaxe est peut-être un peu plus propre.

Réponse originale (valable jusqu'à la bêta 5) :

Je pense que la manière correcte d'implémenter le modèle de délégué est de marquer votre protocole avec l'attribut class_protocol

Extrait du livre sur le langage de programmation Swift :

"classe_protocole Appliquez cet attribut à un protocole pour indiquer que le protocole ne peut être adopté que par des types de classe. Si vous appliquez l'attribut objc à un protocole, l'attribut class_protocol est implicitement appliqué à ce protocole ; il n'est pas nécessaire de marquer explicitement le protocole avec l'attribut class_protocol."

Si j'ai bien compris, en utilisant cet attribut, vous garantissez que ce protocole sera utilisé uniquement sur les classes et pas sur d'autres choses comme les enums ou les structs. De plus, vous pouvez voir que l'attribut objc implique l'attribut class_protocol, c'est pourquoi il fonctionne lorsque vous l'utilisez.

Pour mettre un exemple de code :

@class_protocol protocol ProtocolNameDelegate {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

12 votes

Quelle est la manière correcte de faire des délégués faibles maintenant en Swift ? La documentation d'Apple ne montre pas ou ne déclare pas le délégué comme faible dans son code d'exemple : developer.apple.com/library/ios/documentation/swift/conceptual/

0 votes

@JimT, cela vous donne-t-il encore un crash ? Votre commentaire a reçu beaucoup de votes positifs, mais je me demande si c'est seulement dans les anciennes versions de Swift. Je n'ai jamais eu de crash avec Swift 2 ou 3.

0 votes

Re : "seulement sur les classes et pas d'autres trucs comme les enums ou les structs" quelqu'un sait quel est le terme approprié pour "trucs" ? types ?

308voto

Suragch Points 197

Réponse complémentaire

Je me suis toujours demandé si les délégués devaient être faibles ou non. Récemment, j'en ai appris davantage sur les délégués et sur le moment où il convient d'utiliser des références faibles. Permettez-moi donc d'ajouter quelques points supplémentaires pour les futurs utilisateurs.

  • L'objectif de l'utilisation du weak mot-clé est d'éviter des cycles de référence solides (conserver les cycles). Les cycles de référence forte se produisent lorsque deux instances de classe ont des références fortes l'une à l'autre. Leur nombre de références ne tombe jamais à zéro et elles ne sont donc jamais désallouées.

  • Vous devez seulement utiliser weak si le délégué est une classe. Les structs et les enums Swift sont des types de valeurs (leurs valeurs sont copiées lorsqu'une nouvelle instance est créée), et non des types de référence, ils ne font donc pas de fortes réductions. référence cycles.

  • weak sont toujours facultatives (sinon vous auriez utilisé unowned ) et utilisez toujours var (pas let ) afin que l'option puisse être réglée sur nil lorsqu'il est désalloué.

  • Une classe parent devrait naturellement avoir une référence forte à ses classes enfants et donc ne pas utiliser l'attribut weak mot-clé. Cependant, lorsqu'un enfant veut une référence à son parent, il doit en faire une référence faible en utilisant le mot-clé weak mot-clé.

  • weak doit être utilisé lorsque vous voulez une référence à une classe que vous ne possédez pas, et pas seulement pour un enfant référençant son parent. Lorsque deux classes non hiérarchiques doivent se référer l'une à l'autre, choisissez-en une pour être faible. Le choix dépend de la situation. Voir les réponses à cette question pour en savoir plus.

  • En règle générale, les délégués doivent être marqués en tant que weak parce que la plupart des délégués font référence à des classes qu'ils ne possèdent pas. C'est certainement vrai lorsqu'un enfant utilise un délégué pour communiquer avec un parent. L'utilisation d'une référence faible pour le délégué est ce que l'on appelle la documentation recommande. (Mais voir este aussi.)

  • Les protocoles peuvent être utilisés à la fois pour types de référence (classes) et types de valeurs (structs, enums). Ainsi, dans le cas probable où vous devez rendre un délégué faible, vous devez en faire un protocole objet seulement. La façon de le faire est d'ajouter AnyObject à la liste d'héritage du protocole. (Dans le passé, vous faisiez cela en utilisant la fonction class mot-clé, mais AnyObject est préféré maintenant .)

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }

Étude complémentaire

C'est la lecture des articles suivants qui m'a permis de mieux comprendre. Ils traitent également de questions connexes comme le unowned et les forts cycles de référence qui se produisent avec les fermetures.

Related

5 votes

Tout cela est intéressant, mais n'est pas vraiment lié à ma question initiale, qui ne porte ni sur la faiblesse de l'ARC, ni sur les raisons pour lesquelles les délégués sont généralement faibles. Nous savons déjà tout cela et nous nous posions simplement la question suivante comment vous pouvez déclarer une référence de protocole faible (réponse parfaite de @flainez).

32 votes

Vous avez raison. En fait, je me suis posé la même question que vous tout à l'heure, mais il me manquait beaucoup d'informations de base. J'ai fait la lecture ci-dessus et j'ai pris des notes supplémentaires pour m'aider à comprendre tous les aspects liés à votre question. Je pense maintenant pouvoir appliquer la réponse que vous avez acceptée et savoir pourquoi je le fais. J'espère que cela aidera également les futurs téléspectateurs.

5 votes

Mais puis-je avoir un protocole faible qui ne dépend pas du type ? Un protocole ne se préoccupe pas de savoir quel objet s'y conforme. Une classe ou une structure peuvent donc s'y conformer. Est-il possible d'avoir l'avantage que les deux soient capables de s'y conformer, mais que seuls les types de classe qui s'y conforment soient faibles ?

39voto

Tim Chen Points 236

AnyObject est la manière officielle d'utiliser une référence faible en Swift.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

De Apple :

Pour éviter les cycles de référence forts, les délégués doivent être déclarés en tant que références faibles. Pour plus d'informations sur les références faibles, voir Cycles de référence forte entre instances de classe. Marquage du protocole comme étant réservé à une classe vous permettra ultérieurement de déclarer que le délégué doit utiliser une référence faible. Vous marquez un protocole comme étant de classe seulement en héritant de Tout objet comme indiqué dans la section Protocoles de classe unique.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276

8 votes

Intéressant. Est-ce que class obsolète dans Swift 4.1 ?

0 votes

@hnh Vous pouvez toujours créer un "pseudo-protocole" en en faisant une classe, mais protocol : AnyObject fait exactement ce que demande l'OP avec moins d'effets secondaires que d'en faire une classe. (vous ne pouvez toujours pas utiliser un tel protocole avec des types de valeur, mais le fait de le déclarer comme une classe ne résoudra pas ce problème non plus).

8voto

William Rust Points 351

L'utilisation de @objc est la bonne façon de procéder même si vous n'interagissez pas avec Obj-C. Elle garantit que votre protocole est appliqué à une classe et non à un enum ou à un struct. Voir "Checking for Protocol Conformance" dans le manuel.

0 votes

Comme nous l'avons mentionné, il ne s'agit pas d'une réponse à la question. Un simple programme Swift devrait être capable de se suffire à lui-même sans être lié au NS'isme (ce qui pourrait impliquer de ne plus utiliser les délégués mais une autre construction de conception). Mon MyClass Swift pur ne se soucie pas de savoir si la destination est une structure ou un objet, et je n'ai pas besoin d'optionnels. Peut-être qu'ils pourront corriger cela plus tard, c'est un nouveau langage après tout. Peut-être quelque chose comme "protocole de classe XYZ" si la sémantique de référence est requise ?

4 votes

Je pense qu'il est également utile de noter que \@objc a des effets secondaires supplémentaires - la suggestion de NSObjectProtocol de @eXhausted est un peu meilleure. Avec \@objc - si le délégué de classe prend un argument objet, comme 'handleResult(r : MySwiftResultClass)', la MySwiftResultClass doit maintenant hériter de NSObject ! Et on peut supposer qu'il n'y a plus d'espace de nom non plus, etc. En bref : \@objc est une fonctionnalité de transition, pas une fonctionnalité de langage.

0 votes

Je pense qu'ils ont résolu ce problème. Vous écrivez maintenant : protocol MyClassDelegate : class { }

1voto

Duncan C Points 18661

Le qualificatif faible ne s'applique qu'aux référence objets. À moins que vous n'ajoutiez le @objc , AnyObject ou class sur votre protocole, l'objet conforme au protocole peut ne pas être un objet de référence.

Ainsi, vous avez besoin d'un de ces qualificatifs (et de AnyObject est recommandé, car class est censé être déprécié).

Au fait, notez que l'ajout de @objc à vos classes et propriétés est parfois nécessaire, même dans les applications "purement Swift". Cela n'a rien à voir avec votre langage de développement. Il oblige le compilateur à construire votre code d'une manière compatible avec le runtime Objective-C, ce qui est nécessaire pour certaines interfaces du système d'exploitation (target/action et les chemins de clés de l'ancien style, par exemple).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X