Swift a:
En quoi une référence non possédée est-elle différente d'une référence faible?
Quand est-il prudent d'utiliser une référence non possédée?
Les références non possédées constituent-elles un risque pour la sécurité comme pointeurs en suspens en C/C++?
Les références weak
et unowned
ne créent pas de maintien strong
sur l'objet référencé (elles n'augmentent pas le nombre de rétentions afin d'empêcher ARC de libérer l'emplacement de l'objet référencé).
Mais pourquoi deux mots-clés? Cette distinction est liée au fait que les types Optional
sont intégrés au langage Swift. Longue histoire à leur sujet: types facultatifs offre la sécurité de la mémoire (cela fonctionne à merveille avec règles du constructeur de Swift - qui sont strictes afin de fournir cet avantage).
Une référence weak
permet de devenir nil
(cela se produit automatiquement lorsque l'objet référencé est désalloué). Par conséquent, le type de votre propriété doit être facultatif - vous êtes donc obligé en tant que programmeur pour le vérifier avant de l’utiliser (le compilateur vous oblige autant que possible à écrire du code sécurisé).
Une référence unowned
suppose qu'elle ne deviendra jamais nil
au cours de sa vie. 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, d'une manière ou d'une autre, l'objet référencé est désalloué, l'application se bloque lorsque la référence non possédée est utilisée.
De la documentation Apple :
Utilisez une référence faible chaque fois que cela est valide pour que cette référence devienne nulle à un moment donné de son existence. 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.
La documentation contient des exemples traitant des cycles de conservation et de leur rupture. Tous ces exemples sont extraits de the docs .
Exemple pour le mot clé weak
:
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 certains ASCII art (vous devriez aller voir la documentation - ils ont de jolis diagrammes):
Person ===(strong)==> Apartment
Person <==(weak)===== Apartment
Les exemples Person
et Apartment
illustrent une situation dans laquelle deux propriétés, dont les deux sont autorisées à être nulles, peuvent générer un cycle de référence puissant. Ce scénario est mieux résolu avec une référence faible. Les deux entités peuvent exister sans dépendance stricte l'une par rapport à l'autre.
Exemple pour le mot clé unowned
:
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, une Customer
peut avoir ou non une CreditCard
, mais une CreditCard
sera toujours toujours associé à un Customer
. Pour représenter cela, la classe Customer
a une propriété facultative card
, mais la classe CreditCard
a une propriété non optionnelle (et non possédée) customer
.
Customer ===(strong)==> CreditCard
Customer <==(unowned)== CreditCard
L'exemple Customer
et CreditCard
montre une situation dans laquelle une propriété autorisée à être nulle et une autre propriété ne pouvant pas l'être ont le potentiel de générer un cycle de référence fort. Ce scénario est mieux résolu avec une référence non propriétaire.
Note d'Apple:
Les références faibles doivent être déclarées en tant que variables, pour indiquer que leur valeur peut changer à 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 dans lequel les deux propriétés devraient toujours avoir une valeur et aucune propriété ne devrait jamais être nulle une fois l'initialisation terminée.
Et il existe également les scénarios classiques du cycle de conservation à éviter lorsque vous travaillez avec des fermetures.
Pour cela, je vous encourage à visiter le documentation Apple , ou à lire le livre .
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 un contrôle fort sur l'instance à laquelle elle fait référence et n'empêche donc pas ARC de supprimer l'instance référencée. Étant donné que les références faibles ne peuvent avoir "aucune valeur", vous devez déclarer chaque référence faible comme ayant un type facultatif. (Apple Docs)
Référence non possédée:
A l'instar des références faibles, une référence non possédée ne conserve pas une emprise forte sur l'instance à laquelle elle fait référence. Contrairement à une référence faible, cependant, une référence non possédée est supposée avoir toujours une valeur. De ce fait, 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 que cela est valide pour que cette référence devienne nulle à un moment donné de son existence. 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 prudent d'utiliser une "référence non possédée"?
Comme cité ci-dessus, une référence non possédée est supposée avoir toujours une valeur. Vous ne devriez donc l'utiliser que lorsque vous êtes sûr que la référence ne sera jamais nulle. Apple Les documents illustrent un scénario d'utilisation pour les références non possédées à l'aide de l'exemple suivant.
Supposons que nous ayons deux classes Customer
et 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. Donc, ils devraient avoir la relation suivante:
class Customer {
var card: CreditCard?
}
class CreditCard {
unowned let customer: Customer
}
Q3. Les "références non possédées" font-elles référence à un risque de sécurité tel que les "pointeurs en suspens" 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 dont la valeur est garantie, il ne devrait en aucun cas être un risque pour la sécurité. Toutefois, si vous essayez d'accéder à une référence non possédée après la désallocation de l'instance à laquelle elle fait référence, vous déclencherez une erreur d'exécution et l'application se bloquera.
C'est le seul risque que je vois avec ça.
Si self peut être nul dans l'utilisation de la fermeture, utilisez [moi faible] .
Si self ne sera jamais nul dans l'utilisation de la fermeture, utilisez [moi non possédé] .
Si cela se bloque lorsque vous utilisez [moi non possédé] , alors self est probablement nul à un moment donné de cette fermeture et vous devrez probablement utiliser [moi faible] à la place.
Découvrez les exemples d'utilisation fort , faible et non possédée dans les fermetures:
Extraits de lien
Quelques points de conclusion
Les références non possédées sont une sorte de référence faible utilisée dans le cas d'une relation de la même durée de vie entre deux objets, lorsqu'un objet ne doit jamais 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 intermédiaire Swift WWDC, une personne possède une carte de crédit et une carte de crédit ne peut avoir qu'un seul détenteur. Sur la carte de crédit, la personne ne doit pas être une propriété facultative, car vous ne voulez pas avoir une carte de crédit flottante avec un seul propriétaire. Vous pouvez rompre ce cycle en faisant de la propriété du titulaire du crédit une référence faible, mais vous devez également la rendre facultative et variable (par opposition à constante). La référence non propriétaire dans ce cas signifie que même si CreditCard n’a pas de participation dans une personne, sa vie en dépend.
class Person {
var card: CreditCard?
}
class CreditCard {
unowned let holder: Person
init (holder: Person) {
self.holder = holder
}
}
ARC
ARC est une fonctionnalité de compilation qui constitue la version d'Apple de la gestion automatisée de la mémoire. Il représente le comptage automatique des références. Cela signifie que cela seulement libère de la mémoire pour les objets quand il y a zéro fort leurs références.
FORT
C'est essentiellement une référence normale (pointeur et tout), mais c'est spécial en ce sens qu'elle protège l'objet référé d'être désalloué par ARC en augmentant il conserve le compte par 1. En substance, tant que rien a une forte référence à un objet, il ne sera pas désalloué.
En règle générale, nous pouvons utiliser des références fortes lorsque les relations hiérarchiques des objets sont linéaires . Lorsqu'une hiérarchie de références fortes circule de parent à enfant, il est toujours correct d'utiliser des références fortes.
Résolution des cycles de référence puissants entre les instances de classe
Les emplacements importants à utiliser weak
et unowned
sont des variables lorsque vous avez des cycles de conservation potentiels. Un cycle de conservation correspond à ce qui se produit lorsque deux objets sont fortement référencés. Si 2 objets sont fortement référencés, ARC ne générera pas le code de message de libération approprié sur chaque instance car ils se maintiennent en vie.
FAIBLE
Une référence faible est simplement un pointeur sur un objet qui ne protège pas la suppression de la désaffectation de l'objet par ARC. Alors que les références fortes augmentent le nombre de rétention d'un objet de 1, les références faibles ne le font pas . De plus, les références faibles remettent à zéro le pointeur sur votre objet lorsqu'il désalloue avec succès. Cela garantit que lorsque vous accédez à une référence faible, il s'agira soit d'un objet valide, soit de nil
.
ARC définit automatiquement une référence faible sur nil lorsque l'instance à laquelle il fait référence est désallouée. Et, comme les références faibles doivent permettre la modification de leur valeur en
nil
au moment de l'exécution, elles sont toujours déclarées comme variables
Utiliser
Dans une situation où deux propriétés, qui sont toutes deux autorisées à être nil
. Ce scénario est mieux résolu avec une référence weak
.
Utilisez une référence weak
lorsque l'autre instance a une durée de vie plus courte , c'est-à-dire lorsque l'autre instance peut être désallouée en premier.
SANS PROPRIÉTAIRE
Les références non possédées, comme les références faibles, n'augmentent pas le nombre de retenues de l'objet référencé. Les références non possédées ne sont pas à zéro. Cela signifie que lorsque l'objet est désalloué, il ne met pas le pointeur à zéro. Cela signifie que l'utilisation de références non propriétaires peut, dans certains cas, conduire à pointeurs en suspens .
ARC ne définit jamais la valeur nil d'une référence non possédée, ce qui signifie que les références non possédées sont définies à l'aide de types non facultatifs .
IMPORTANT.
Utilisez une référence non possédée uniquement lorsque vous êtes sûr que la référence fait toujours référence à une instance qui n'a pas été désallouée.
Si vous essayez d'accéder à la valeur d'une référence non possédée après la désallocation de cette instance, vous obtiendrez une erreur d'exécution -
Attempted to read an unowned reference but object was already deallocated
REMARQUE
Swift fournit également des références non propriétaires non sécurisées dans les cas où vous devez désactiver les vérifications de sécurité de l'exécution, par exemple pour des raisons de performances. Comme pour toutes les opérations dangereuses, vous prenez la responsabilité de vérifier ce code pour la sécurité.
Vous indiquez une référence non propriétaire non sécurisée en écrivant
unowned(unsafe)
. Si vous essayez d'accéder à une référence non propriétaire non sécurisée après la désallocation de l'instance à laquelle elle fait référence, votre programme essaiera d'accéder à l'emplacement de la mémoire où se trouvait l'instance, ce qui constitue une opération non sécurisée.
Utiliser
Dans une situation où une propriété qui est autorisée à être nil
et une autre propriété qui ne peut pas être nil
. Ce scénario est mieux résolu avec une référence unowned
.
Utilisez une référence unowned
lorsque l'autre instance a la durée de vie identique ou un plus long à vie. Tout comme un implicitement non emballé , si vous pouvez garantir que la référence ( ne sera pas être nil
à son point d'utilisation, utilisez unowned
. Sinon, vous devriez utiliser weak
.