web-dev-qa-db-fra.com

Comment puis-je créer une référence de protocole faible en 'pure' Swift (sans @objc)

Les références weak ne semblent pas fonctionner dans Swift à moins qu'une protocol ne soit déclarée comme @objc, ce que je ne veux pas dans un pure Swift app.

Ce code donne une erreur de compilation (weak ne peut pas être appliqué à un type de classe non-_ MyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

J'ai besoin de préfixer le protocole avec @objc, alors cela fonctionne.

Question: Quel est le "pur" Swift moyen d'accomplir une weakdelegate?

537
hnh

Vous devez déclarer le type de protocole comme class.

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

Si j'ai bien compris, en utilisant class, vous garantissez que ce protocole ne sera utilisé que sur des classes et pas d'autres éléments tels que des énumérations ou des structs.

989
flainez

Réponse supplémentaire

J'avais toujours du mal à savoir 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 le moment d'utiliser des références faibles, alors permettez-moi d'ajouter quelques points supplémentaires ici pour le bien des futurs téléspectateurs.

  • Le mot-clé weak a pour but d'éviter cycles de référence élevés (cycles de rétention). Les cycles de référence forts se produisent lorsque deux instances de classe ont de fortes références l'une à l'autre. Leur nombre de références ne va jamais à zéro, ils ne sont donc jamais désalloués.

  • Vous devez uniquement utiliser weak si le délégué est une classe. Swift les structures et les énumérations sont des types de valeur (leurs valeurs sont copiées lors de la création d'une nouvelle instance), pas des types de référence, elles ne font donc pas une forte reference cycles.

  • Les références weak sont toujours facultatives (sinon, vous utiliseriez unowned) et vous utiliseriez toujours var (et non let) de sorte que l'option (optionnelle) puisse être définie sur nil lorsque il est désalloué.

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

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

  • En règle générale, les délégués doivent être marqués comme weak car la plupart des délégués font référence à des classes qu'ils ne possèdent pas. Cela est certainement vrai lorsqu'un enfant utilise un délégué pour communiquer avec un parent. Utiliser une référence faible pour le délégué correspond à ce que documentation recommande. (Mais voir this , aussi.)

  • Les protocoles peuvent être utilisés pour types de référence (classes) et types de valeur (structs, enums). Ainsi, dans le cas probable où vous aurez besoin d'affaiblir un délégué, vous devez en faire un protocole d'objet uniquement. Pour ce faire, ajoutez AnyObject à la liste d'héritage du protocole. (Dans le passé, vous utilisiez le mot clé class, mais AnyObject est préférable maintenant .)

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

Une étude plus approfondie

La lecture des articles suivants est ce qui m'a aidé à comprendre ceci beaucoup mieux. Ils abordent également des questions connexes telles que le mot-clé unowned et les cycles de référence importants qui se produisent lors des fermetures.

Apparenté, relié, connexe

258
Suragch

AnyObject est le moyen officiel d'utiliser une référence faible dans Swift.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

De Apple:

Pour éviter des cycles de référence forts, les délégués doivent être déclarés comme références faibles. Pour plus d'informations sur les références faibles, consultez Cycles de référence élevés entre les instances de classe. Si vous marquez le protocole en tant que classe uniquement, vous pourrez plus tard déclarer que le délégué doit utiliser une référence faible. Vous marquez un protocole comme étant une classe uniquement en héritant de AnyObject , comme indiqué dans la section Protocoles de classe uniquement.

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

34
Tim Chen

Mise à jour: Il semble que le manuel ait été mis à jour et que l'exemple auquel je faisais référence ait été supprimé. Voir la réponse à modifier de @ flainez ci-dessus.

Original: Utiliser @objc est la méthode la plus appropriée, même si vous n'interagissez pas avec Obj-C. Cela garantit que votre protocole est appliqué à une classe et non à une énumération ou à une structure. Voir "Vérification de la conformité du protocole" dans le manuel.

9
William Rust