Quelle est la différence entre une référence faible et une référence non possédée?


240

Swift a:

  • Références solides
  • Références faibles
  • Références inconnues

En quoi une référence non possédée est-elle différente d'une référence faible?

Quand est-il sécuritaire d'utiliser une référence sans propriétaire?

Les références non possédées constituent-elles un risque pour la sécurité comme les pointeurs pendants en C / C ++?


3
Très bon article sur andrewcbancroft.com/2015/05/08/…
Zeeshan

Mon expérience est d'utiliser unownedpour les classes que nous contrôlons, pour les classes Apple, utiliser weakparce que nous ne pouvons pas garantir avec certitude ce qu'il fait
onmyway133

@NoorAli, ou "ownBy", car la référence "sans propriétaire" pointe souvent vers le propriétaire.
Ian Ringrose du

1
REMARQUE: Il y a des implications de performances importantes à connaître avec chacune de ces références: stackoverflow.com/questions/58635303/…
Epic Byte

@EpicByte Parfois, un GC complet comme Java ou C # vaut la surcharge.
Ian Ringrose

Réponses:


361

Les références weaket unownedne créent pas de strongblocage sur l'objet référencé (c'est-à-dire qu'elles n'augmentent pas le nombre de rétentions afin d'empêcher ARC de désallouer l'objet référencé).

Mais pourquoi deux mots-clés? Cette distinction tient au fait que les Optionaltypes sont intégrés dans le langage Swift. Longue histoire à leur sujet: les types facultatifs offrent une sécurité de la mémoire (cela fonctionne à merveille avec les règles du constructeur de Swift - qui sont strictes afin de fournir cet avantage).

Une weakréférence permet la possibilité de le devenir nil(cela se produit automatiquement lorsque l'objet référencé est désalloué), donc le type de votre propriété doit être facultatif - donc vous, en tant que programmeur, êtes obligé de le vérifier avant de l'utiliser (fondamentalement le le compilateur vous force, autant que possible, à écrire du code sûr).

Une unownedréférence suppose qu'elle ne deviendra jamais de nilson vivant. Une référence non possédée doit être définie lors de l'initialisation - cela signifie que la référence sera définie comme un type non facultatif pouvant être utilisé en toute sécurité sans vérification. Si l'objet auquel il est fait référence est désalloué, l'application se bloque lorsque la référence sans propriétaire est utilisée.

Depuis les documents Apple :

Utilisez une référence faible chaque fois qu'il est valide pour que cette référence devienne nulle à un moment donné au cours de sa durée de vie. Inversement, utilisez une référence non possédée lorsque vous savez que la référence ne sera jamais nulle une fois définie lors de l'initialisation.

Dans les documents, il y a quelques exemples qui discutent des cycles de conservation et comment les briser. Tous ces exemples sont extraits de la documentation .

Exemple du weakmot - clé:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    weak var tenant: Person?
}

Et maintenant, pour un peu d'art ASCII (vous devriez aller voir les documents - ils ont de jolis diagrammes):

Person ===(strong)==> Apartment
Person <==(weak)===== Apartment

L' exemple Personet Apartmentmontre une situation dans laquelle deux propriétés, toutes deux autorisées à être nulles, ont le potentiel de provoquer un cycle de référence fort. Ce scénario est mieux résolu avec une référence faible. Les deux entités peuvent exister sans dépendre strictement l'une de l'autre.

Exemple du unownedmot - clé:

class Customer {
    let name: String
    var card: CreditCard?
    init(name: String) { self.name = name }
}

class CreditCard {
    let number: UInt64
    unowned let customer: Customer
    init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
}

Dans cet exemple, un Customerpeut ou non avoir un CreditCard, mais un CreditCard sera toujours associé à un Customer. Pour représenter cela, la Customerclasse a une cardpropriété facultative , mais la CreditCardclasse a une propriété non facultative (et sans customerpropriétaire ) .

Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard

L' exemple Customeret CreditCardmontre une situation dans laquelle une propriété autorisée à être nulle et une autre propriété qui ne peut pas être nulle ont le potentiel de provoquer un cycle de référence puissant. Ce scénario est mieux résolu avec une référence sans propriétaire.

Remarque d'Apple:

Les références faibles doivent être déclarées en tant que variables, pour indiquer que leur valeur peut changer au moment de l'exécution. Une référence faible ne peut pas être déclarée comme constante.

Il existe également un troisième scénario où les deux propriétés doivent toujours avoir une valeur, et aucune propriété ne doit jamais être nulle une fois l'initialisation terminée.

Et il existe également les scénarios de cycle de rétention classiques à éviter lorsque vous travaillez avec des fermetures.

Pour cela, je vous encourage à visiter la documentation Apple ou à lire le livre .


3
C'est un peu trivial mais je trouve l'exemple de l'appartement et de la personne quelque peu déroutant qui présente également une solution supplémentaire pour briser le cycle de référence fort. L'appartement d'une personne est facultatif et peut donc être nul ainsi que le locataire d'un appartement est facultatif et peut donc être nul, de sorte que les deux propriétés peuvent être définies comme faibles. ``
Justin Levi Winter

class Person {let name: String init (name: String) {self.name = name} faible var appartement: Appartement? } Appartement de classe {let numéro: Int init (numéro: Int) {self.number = nombre} locataire var faible: Personne? }
Justin Levi Winter

3
Quelle est la différence entre les weak var Person?contre var Person??
Dean

4
@JustinLevi, Si vous déclarez les deux propriétés faibles, il est possible qu'elles soient désallouées. La personne conserve une référence forte à l'appartement afin que l'appartement ne soit pas désalloué. Si l'appartement avait la même référence forte envers la personne, ils créeraient un cycle de rétention - qui peut être interrompu par le programmeur au moment de l'exécution s'il le sait, mais sinon c'est juste une fuite de mémoire. C'est toute cette histoire de fort, de faible et de non propriétaire: la gestion de la mémoire à un niveau supérieur, car ARC fait tout le sale boulot pour nous. Éviter les cycles de rétention est notre travail.
Ilea Cristian

1
Est-ce que le seul avantage de ce qui est inconnu est que vous n'avez pas besoin de déballer et que vous pouvez utiliser une constante? Y a-t-il un cas où vous ne pourriez pas utiliser faible et ne pourriez utiliser que sans propriétaire?
Alan

29

Q1. En quoi une «référence non possédée» est-elle différente d'une «référence faible»?

Référence faible:

Une référence faible est une référence qui ne maintient pas une forte emprise sur l'instance à laquelle elle se réfère, et n'empêche donc pas ARC de supprimer l'instance référencée. Étant donné que les références faibles peuvent avoir «aucune valeur», vous devez déclarer chaque référence faible comme ayant un type facultatif. (Apple Docs)

Référence sans propriétaire:

Comme les références faibles, une référence non possédée ne garde pas une forte emprise sur l'instance à laquelle elle se réfère. Contrairement à une référence faible, cependant, une référence non possédée est supposée avoir toujours une valeur. Pour cette raison, une référence non possédée est toujours définie comme un type non facultatif. (Apple Docs)

Quand utiliser chacun:

Utilisez une référence faible chaque fois qu'il est valide pour que cette référence devienne nulle à un moment donné au cours de sa durée de vie. Inversement, utilisez une référence non possédée lorsque vous savez que la référence ne sera jamais nulle une fois définie lors de l'initialisation. (Apple Docs)


Q2. Quand est-il sécuritaire d'utiliser une «référence sans propriétaire»?

Comme indiqué ci-dessus, une référence non possédée est supposée avoir toujours une valeur. Vous ne devez donc l'utiliser que lorsque vous êtes sûr que la référence ne sera jamais nulle. Apple Docs illustre un cas d'utilisation pour les références non possédées à travers l'exemple suivant.

Supposons que nous ayons deux classes Customeret CreditCard. Un client peut exister sans carte de crédit, mais une carte de crédit n'existera pas sans client, c'est-à-dire qu'on peut supposer qu'une carte de crédit aura toujours un client. Ainsi, ils devraient avoir la relation suivante:

class Customer {
    var card: CreditCard?
}

class CreditCard {
    unowned let customer: Customer
}

Q3. Les références «sans référence» constituent-elles un risque pour la sécurité comme les «pointeurs pendants» en C / C ++

Je ne pense pas.

Étant donné que les références non possédées ne sont que des références faibles qui sont garanties d'avoir une valeur, cela ne devrait en aucun cas constituer un risque pour la sécurité. Cependant, si vous essayez d'accéder à une référence non possédée après que l'instance à laquelle elle fait référence est désallouée, vous déclencherez une erreur d'exécution et l'application se bloquera.

C'est le seul risque que je vois avec ça.

Lien vers Apple Docs


votre exemple de programme Q2 simple à comprendre à propos des propriétaires ... merci ... pouvez-vous ajouter le même type d'exemple pour les faibles et les forts ..
Ranjith Kumar

Excellent. Je vous remercie.
Swifty McSwifterton

Pouvez-vous inclure un exemple courant pour les personnes sans propriétaire ou faibles?
Honey

Considérez les objets parent et enfant, si l'enfant ne peut pas exister sans parent, utilisez-le unownedpour la propriété parent dans la classe enfant. faible est vice versa. Belle explication @myxtic! unownedles références ne sont que des weakréférences dont la valeur est garantie!
Saif

26

Si le self peut être nul dans la fermeture, utilisez [self faible] .

Si l' auto ne sera jamais nul dans la fermeture, utilisez [auto sans propriétaire] .

S'il se bloque lorsque vous utilisez [auto sans propriétaire], alors self est probablement nul à un moment donné de cette fermeture et vous devez probablement utiliser [self self] à la place.

Découvrez les exemples d'utilisation de fermetures fortes , faibles et sans propriétaire :

https://developer.apple.com/library/ios/documentation/swift/conceptual/swift_programming_language/AutomaticReferenceCounting.html


7
Pourquoi ne pas utiliser simplement faible même si soi ne peut jamais être nul, aucun mal n'est fait correctement?
Boon

4
salut @Boon - c'est en effet la question critique.
Fattie

[self faible] => Si j'utilise la fermeture à l'intérieur de viewDidLoad (), comment peut-il selfêtre nul?
Hassan Tareq

@HassanTareq, je pense que quelques bons exemples sont mentionnés dans l'article mentionné ci-dessus. Consultez la section «Résolution des cycles de référence forts pour les fermetures», en particulier. Quote: "Swift vous oblige à écrire self.someProperty ou self.someMethod () (plutôt que juste someProperty ou someMethod ()) chaque fois que vous faites référence à un membre de self dans une fermeture. Cela vous aide à vous rappeler qu'il est possible de se capturer par accident." Extrait de: Apple Inc. «Le langage de programmation Swift (Swift 4)». iBooks. itunes.apple.com/de/book/the-swift-programming-language-swift-4/… "
Nick Entin

1
@Boon Si vous utilisez toujours faible, le compilateur forcera à vérifier facultatif avant utilisation. Si vous n'avez pas mis cette vérification, cela donnera une erreur de temps de compilation. Il n'y a pas d'autre Harm.
Vikas Mishra

5

Extraits du lien

Quelques points finaux

  • Pour déterminer si vous devez même vous soucier des forts, des faibles ou des inconnus, demandez: «Suis-je en train de traiter avec des types de référence». Si vous travaillez avec Structs ou Enums, ARC ne gère pas la mémoire de ces types et vous n'avez même pas à vous soucier de spécifier faible ou non propriétaire pour ces constantes ou variables.
  • Les références fortes conviennent parfaitement dans les relations hiérarchiques où le parent fait référence à l'enfant, mais pas l'inverse. En fait, les références fortes sont le type de référence le plus approprié la plupart du temps.
  • Lorsque deux instances sont éventuellement liées l'une à l'autre, assurez-vous que l'une de ces instances contient une référence faible à l'autre.
  • Lorsque deux instances sont liées de manière à ce que l'une des instances ne puisse exister sans l'autre, l'instance avec la dépendance obligatoire doit contenir une référence sans propriétaire à l'autre instance.

1

Les références weaket unownedn'affecteront pas le nombre de références de l'objet. Mais une référence faible sera toujours facultative, c'est-à-dire qu'elle peut être nulle, alors que les unownedréférences ne peuvent jamais être nulles, elles ne seront donc jamais facultatives. Lorsque vous utilisez une référence facultative, vous devrez toujours gérer la possibilité que l'objet soit nul. En cas de référence non possédée, vous devrez vous assurer que l'objet n'est jamais nul. L'utilisation d'une référence non propriétaire à un objet nil sera similaire à un déballage forcé d'une option nil.

Cela dit, il est sûr d'utiliser une référence sans propriétaire où vous êtes sûr que la durée de vie de l'objet est supérieure à celle de la référence. Si ce n'est pas le cas, il vaut mieux utiliser une référence faible à la place.

Quant à la troisième partie de la question, je ne pense pas qu'une référence sans propriétaire soit similaire à un pointeur suspendu. Lorsque nous parlons de comptage de référence, nous nous référons généralement à un comptage de référence fort de l'objet. De même, Swift conserve un nombre de références non possédées et un nombre de références faible pour l'objet (une référence faible pointe vers quelque chose appelé "table d'appoint" plutôt que l'objet lui-même). Lorsque le nombre de références fort atteint zéro, l'objet est réinitialisé, mais il ne peut pas être désalloué si le nombre de références non possédées est supérieur à zéro.

Maintenant, un pointeur suspendu est quelque chose qui pointe vers un emplacement mémoire qui a déjà été désalloué. Mais en un rien de temps puisque la mémoire ne peut être désallouée que tant qu'il y a une référence inconnue à l'objet, elle ne peut pas provoquer un pointeur pendant.

Il existe de nombreux articles qui traitent de la gestion rapide de la mémoire plus en détail. En voici un.


0

Les références non possédées sont une sorte de référence faible utilisée dans le cas d'une relation à durée de vie identique entre deux objets, lorsqu'un objet ne doit appartenir qu'à un autre objet. C'est un moyen de créer une liaison immuable entre un objet et l'une de ses propriétés.

Dans l'exemple donné dans la vidéo SWDC intermédiaire rapide, une personne possède une carte de crédit, et une carte de crédit ne peut avoir qu'un seul titulaire. Sur la carte de crédit, la personne ne doit pas être une propriété facultative, car vous ne voulez pas qu'une carte de crédit flotte avec un seul propriétaire. Vous pouvez briser ce cycle en faisant de la propriété du titulaire du crédit une référence faible, mais cela vous oblige également à la rendre facultative et variable (par opposition à constante). La référence sans propriétaire dans ce cas signifie que bien que CreditCard ne possède pas de participation dans une personne, sa durée de vie en dépend.

class Person {
    var card: CreditCard?
}

class CreditCard {

    unowned let holder: Person

    init (holder: Person) {
        self.holder = holder
    }
}

lien vers la vidéo ou le titre wwdc?
Osa

-2

Utilisez unownedlorsque vous êtes sûr que selfvous ne pouvez jamais être nilau point selfauquel vous accédez à ce moment-là.

Exemple (vous pouvez bien sûr ajouter la cible directement depuis MyViewController, mais encore une fois, c'est un exemple simple):

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myButton = MyButton { [unowned self] in
            print("At this point, self can NEVER be nil. You are safe to use unowned.")
            print("This is because myButton can not be referenced without/outside this instance (myViewController)")
        }
    }
}

class MyButton: UIButton {
    var clicked: (() -> ())

    init(clicked: (() -> ())) {
        self.clicked = clicked

        // We use constraints to layout the view. We don't explicitly set the frame.
        super.init(frame: .zero)

        addTarget(self, action: #selector(clicked), for: .touchUpInside)
    }

    @objc private func sendClosure() {
        clicked()
    }
}

Utiliser weakquand il y a une possibilité selfpeut être nilau point auquel vous accédez self.

Exemple:

class MyViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        NetworkManager.sharedInstance.receivedData = { [weak self] (data) in
            print("Can you guarentee that self is always available when the network manager received data?")
            print("Nope, you can't. Network manager will be alive, regardless of this particular instance of MyViewController")
            print("You should use weak self here, since you are not sure if this instance is still alive for every")
            print("future callback of network manager")
        }
    }
}

class NetworkManager {

    static let sharedInstance = NetworkManager()

    var receivedData: ((Data) -> ())?

    private func process(_ data: Data) {
        // process the data...

        // ... eventually notify a possible listener.
        receivedData?(data)
    }
}

Contre unowned:

  • Plus efficace que faible
  • Vous pouvez (enfin, vous êtes forcé) de marquer l'instance comme immuable (plus depuis Swift 5.0).
  • Indique au lecteur de votre code: Cette instance a une relation avec X et elle ne peut pas vivre sans, mais si X est parti, je suis parti aussi.

Contre weak:

  • Plus sûr que sans propriétaire (car il ne peut pas planter).
  • Peut créer une relation avec X qui va dans les deux sens, mais les deux peuvent vivre sans l'autre.

Si vous n'êtes pas sûr, utilisez weak. Attendez , je veux dire ici sur StackOverflow ce que vous devez faire dans votre cas! Utiliser faible tout le temps alors que vous ne devriez pas le faire est tout simplement déroutant pour vous et le lecteur de votre code.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.