Non, ce n'est pas sûr.
Comme le souligne @Hamish dans un commentaire ci-dessous, l'ingénieur compilateur Swift Joe Groff décrit qu'il n'y a aucune garantie qu'une référence forte soit maintenue pendant la durée de l'évaluation de l'ERS [ c'est moi qui souligne ]
Confirmation de l'ordre des opérations
Rod_Brown :
Bonjour,
Je m'interroge sur la sécurité d'un type d'accès sur une variable faible :
class MyClass {
weak var weakProperty: MyWeakObject?
func perform() {
// Case 1
weakProperty?.variable = weakProperty!.otherVariable
// Case 2
weakProperty?.performMethod(weakProperty!)
}
}
Avec les deux cas ci-dessus, est-il garanti par Swift que l'option weakProperty
peut être déballée de force à ces endroits ?
Je suis curieux de connaître les garanties que Swift donne sur l'accès lors de chaînage optionnel. Par exemple, est-ce que les weakProperty!
Les accesseurs sont garantis ne se déclenchent que si le chaînage optionnel détermine d'abord que la valeur est déjà non nil
?
De plus, la conservation de l'objet faible est-elle garantie pour la durée de cette évaluation, ou la variable faible peut-elle potentiellement être être capable de désallouer entre l'accès optionnel et la méthode appelée ? appelée ?
Joe_Groff :
Ce n'est pas garanti. Les libérations peuvent être optimisées pour se produire plus tôt plus tôt que cela, à tout moment après la dernière utilisation formelle de la référence forte. forte. Puisque la référence forte chargée afin d'évaluer le côté gauche weakProperty?.variable
n'est pas utilisé par la suite, il n'y a rien qui le maintient en vie, donc il pourrait être immédiatement libéré. S'il y a des effets secondaires dans le getter de la variable qui font que l'objet référencé par weakProperty
à désallouer, nil
-en éliminant la référence faible, alors cela causerait le force-unwrap sur le côté droit à échouer . Vous devez utiliser if let pour tester la référence faible, et référencer la référence forte liée par le if let :
if let property = weakProperty {
property.variable = property.otherVariable
property.performMethod(property)
}
Cela devrait être plus sûr et aussi plus efficace, puisque la référence faible est chargée et testée une fois au lieu de quatre.
Étant donné la réponse citée par Joe Groff ci-dessus, ma réponse précédente est sans objet, mais je la laisserai ici comme un voyage peut-être intéressant (bien que raté) dans les profondeurs du compilateur Swift.
Réponse historique aboutissant à un argument final erroné, mais à travers un parcours intéressant, néanmoins.
Je vais baser cette réponse sur mon commentaire à la réponse supprimée de @appzYourLife :
C'est une pure spéculation, mais en considérant le lien assez étroit entre de nombreux développeurs expérimentés du noyau de Swift et la librairie Boost, je suppose que weak
est verrouillée en une référence forte pour la durée de vie de l'expression, si celle-ci assigne/mute quelque chose dans self
tout comme la méthode explicitement utilisée std::weak_ptr::lock()
de son homologue C++.
Examinons votre exemple, dans lequel self
a été capturé par un weak
et n'est pas nil
lors de l'accès au côté gauche de l'expression d'affectation
self?.variable = self!.otherVariable
/* ^ ^^^^^-- what about this then?
|
\-- we'll assume this is a success */
Nous pouvons examiner le traitement sous-jacent de weak
(Swift) dans le moteur d'exécution Swift, swift/include/swift/Runtime/HeapObject.h
spécifiquement :
/// Load a value from a weak reference. If the current value is a
/// non-null object that has begun deallocation, returns null;
/// otherwise, retains the object before returning.
///
/// \param ref - never null
/// \return can be null
SWIFT_RUNTIME_EXPORT
HeapObject *swift_weakLoadStrong(WeakReference *ref);
La clé ici est le commentaire
Si la valeur actuelle est un objet non nul qui a commencé à être désalloué, renvoie null ; sinon, conserve l'objet avant de retourner .
Comme ceci est basé sur un commentaire de code d'exécution backend, c'est encore quelque peu spéculatif, mais je dirais que ce qui précède implique que lors d'une tentative d'accès à la valeur pointée par un weak
cette référence sera en effet conservée comme une référence forte pendant toute la durée de l'appel ( "... jusqu'au retour" ).
Pour essayer de racheter le "quelque peu spéculatif" de la partie ci-dessus, nous pouvons continuer à creuser la manière dont Swift gère l'accès à une valeur via une balise weak
référence. Sur le site Commentaire de @idmean:ci-dessous (en étudiant le code SIL généré pour un exemple comme celui de l'OP:s) nous savons que le swift_weakLoadStrong(...)
est appelée.
Nous allons donc commencer par examiner l'implémentation de la fonction swift_weakLoadStrong(...)
fonction dans swift/stdlib/public/runtime/HeapObject.cpp
et voir où nous en serons à partir de là :
HeapObject *swift::swift_weakLoadStrong(WeakReference *ref) {
return ref->nativeLoadStrong();
}
Nous trouvons que la mise en œuvre de la nativeLoadStrong()
méthode de WeakReference
de swift/include/swift/Runtime/HeapObject.h
HeapObject *nativeLoadStrong() {
auto bits = nativeValue.load(std::memory_order_relaxed);
return nativeLoadStrongFromBits(bits);
}
De le même fichier la mise en œuvre de nativeLoadStrongFromBits(...)
:
HeapObject *nativeLoadStrongFromBits(WeakReferenceBits bits) {
auto side = bits.getNativeOrNull();
return side ? side->tryRetain() : nullptr;
}
En continuant la chaîne d'appel, tryRetain()
est une méthode de HeapObjectSideTableEntry
(qui est essentiel pour la machine à états du cycle de vie des objets ), et nous trouvons son implémentation dans swift/stdlib/public/SwiftShims/RefCount.h
HeapObject* tryRetain() {
if (refCounts.tryIncrement())
return object.load(std::memory_order_relaxed);
else
return nullptr;
}
La mise en œuvre de la tryIncrement()
de la méthode RefCounts
(ici invoqué via une instance d'un type de typedef
sa spécialisation ) peut être trouvé dans le même fichier que ci-dessus :
// Increment the reference count, unless the object is deiniting.
bool tryIncrement() {
...
}
Je pense que le commentaire ici suffit pour que nous utilisions cette méthode comme point final : si l'objet ne se désinite pas (ce que nous avons supposé plus haut qu'il ne le fait pas, puisque la méthode lhs
de l'affectation dans l'exemple de l'OP est supposée être réussie), le nombre de références (fortes) sur l'objet sera augmenté, et une HeapObject
(soutenu par un fort incrément du compte de référence) sera transmis à l'opérateur d'affectation. Nous n'avons pas besoin d'étudier comment la décrémentation correspondante du nombre de références est finalement effectuée à la fin de l'affectation, mais nous savons maintenant, au-delà de toute spéculation, que l'objet associé à l'objet weak
sera conservée comme référence forte pendant toute la durée de vie de l'affectation, à condition qu'elle n'ait pas été libérée/désallouée au moment de l'accès à sa partie gauche (auquel cas sa partie droite ne sera jamais traitée, comme cela a été expliqué dans la section Réponse de @MartinR:s ).
0 votes
Cela peut vous aider stackoverflow.com/questions/24468336/
13 votes
C'est une question intéressante. Up-voté. C'est assez facile de tester le fonctionnement aujourd'hui, mais est-ce que cela garantit toujours Le travail est la question. Ça a l'air suspect et je ne l'utiliserais pas même si je savais que ça marche. C'est assez facile à utiliser
if let
ouguard
pour que vos intentions soient claires.0 votes
Une question encore plus pertinente est la suivante : pourquoi souhaitez-vous utiliser les optionnels de cette manière ?
2 votes
Je ne pense pas que quiconque puisse répondre à la question
Is this always safe?
. Il y a tellement de cas limites avec le filetage. Que faitsomeFunction
faire ? Où est appelé l'achèvement ? Dans tous les cas, ne faites pas ça.0 votes
Pour ma propre information, quelqu'un pourrait-il m'expliquer pourquoi l'expression du côté droit n'est pas évaluée en premier ? Parce que dans ce cas, self étant nul se retrouverait dans
fatal error
1 votes
@MuhammadHassan, la réponse de MartinR ci-dessous aborde cette question.