web-dev-qa-db-fra.com

Devons-nous toujours utiliser [moi non possédé] à l'intérieur de la fermeture dans Swift

Lors de la session 403 de la WWDC 2014 Intermédiaire intermédiaire et transcription , la diapositive suivante

enter image description here

L'orateur a déclaré que dans ce cas, si nous n'utilisions pas [unowned self], ce serait une fuite de mémoire. Est-ce que cela signifie que nous devrions toujours utiliser [unowned self] à l'intérieur de la fermeture?

Sur ligne 64 de ViewController.Swift de l'application Swift Weather) , je n'utilise pas [unowned self]. Mais je mets à jour l'interface utilisateur en utilisant des @IBOutlets tels que self.temperature et self.loadingIndicator. Cela peut aller parce que tous les @IBOutlets que j'ai définis sont weak. Mais pour des raisons de sécurité, devrions-nous toujours utiliser [unowned self]?

class TempNotifier {
  var onChange: (Int) -> Void = {_ in }
  var currentTemp = 72
  init() {
    onChange = { [unowned self] temp in
      self.currentTemp = temp
    }
  }
}
445
Jake Lin

Non, il y a certainement des moments où vous ne voudriez pas utiliser [unowned self]. Parfois, vous voulez que la fermeture se capte afin de vous assurer qu'elle est toujours là au moment où la fermeture est appelée.

Exemple: Demande de réseau asynchrone

Si vous faites une demande réseau asynchrone, vous voulez que la fermeture conserve self pendant la fin de la demande. Cet objet a peut-être été libéré, mais vous souhaitez toujours pouvoir gérer la fin de la demande.

Quand utiliser unowned self ou weak self

Le seul moment où vous voulez vraiment utiliser [unowned self] ou [weak self] est le moment où vous créeriez un cycle de référence fort . Un cycle de référence important se produit lorsqu'il existe une boucle de propriété où les objets se possèdent (par le biais d'une tierce partie) et par conséquent, ils ne seront jamais désalloués car ils s'assurent tous les deux de rester l'un envers l'autre.

Dans le cas spécifique d'une fermeture, vous devez simplement vous rendre compte que toute variable référencée à l'intérieur de celle-ci devient "possédée" par la fermeture. Tant que la fermeture est là, ces objets sont garantis. La seule façon d’arrêter cette propriété est de faire le [unowned self] ou [weak self]. Ainsi, si une classe possède une fermeture et que cette fermeture capture une référence forte à cette classe, vous avez un cycle de référence important entre la fermeture et la classe. Ceci inclut également si la classe possède quelque chose qui possède la fermeture.

Plus précisément dans l'exemple de la vidéo

Dans l'exemple de la diapositive, TempNotifier est propriétaire de la fermeture par le biais de la variable membre onChange. S'ils ne déclaraient pas self comme unowned, la fermeture posséderait également self créant ainsi un cycle de référence fort.

Différence entre unowned et weak

La différence entre unowned et weak est que weak est déclaré en tant qu'option, alors que unowned ne l'est pas. En le déclarant weak, vous pouvez gérer le cas où il pourrait être nul à l'intérieur de la fermeture à un moment donné. Si vous essayez d'accéder à une variable unowned dont la valeur est nulle, le programme entier se bloquera. Donc, utilisez uniquement unowned lorsque vous êtes certain que cette variable sera toujours présente pendant la fermeture.

831
drewag

Mise à jour 11/2016

J'ai écrit un article sur cette extension de cette réponse (en cherchant dans SIL pour comprendre ce que fait ARC), jetez-y un œil ici .

Réponse originale

Les réponses précédentes ne donnent pas vraiment de règles claires sur quand utiliser l'une sur l'autre et pourquoi, alors laissez-moi ajouter quelques choses.

La discussion sans propriétaire ou faible se résume à une question de durée de vie de la variable et de la clôture qui la référence.

Swift weak vs unowned

Scénarios

Vous pouvez avoir deux scénarios possibles:

  1. La fermeture a la même durée de vie que la variable, donc la fermeture sera accessible seulement jusqu'à ce que la variable soit accessible. La variable et la fermeture ont la même durée de vie. Dans ce cas, vous devez déclarer la référence comme sans propriétaire . Un exemple courant est le [unowned self] utilisé dans de nombreux exemples de petites fermetures qui font quelque chose dans le contexte de leur parent et qui ne sont référencées nulle part ailleurs ne survivent pas à leurs parents.

  2. La durée de vie de la fermeture étant indépendante de celle de la variable, la fermeture peut toujours être référencée lorsque la variable n'est plus accessible. Dans ce cas, vous devez déclarer la référence comme faible et vérifier que ce n'est pas nul avant de l'utiliser (ne pas forcer le déroulement). Un exemple courant est le [weak delegate] que vous pouvez voir dans certains exemples de fermeture faisant référence à un objet délégué totalement indépendant (durée de vie).

Utilisation réelle

Alors, que utiliserez-vous/devriez-vous utiliser la plupart du temps?

citant Joe Groff de Twitter :

Sans propriétaire est plus rapide et permet l’immuabilité et la non-optionnalité.

Si vous n'avez pas besoin de faible, ne l'utilisez pas.

Vous trouverez plus d'informations sur unowned* fonctionnement interne ici .

*Généralement aussi appelé non propriétaire (sûr) pour indiquer que les vérifications à l'exécution (qui entraînent un plantage pour les références non valides) sont effectuées avant d'accéder à la référence non possédée.

177
Umberto Raimondi

J'ai pensé ajouter des exemples concrets spécifiques à un contrôleur de vue. Beaucoup d'explications, pas seulement ici sur Stack Overflow, sont vraiment bonnes, mais je travaille mieux avec des exemples du monde réel (@drewag a bien démarré à ce sujet):

  • Si vous avez une fermeture pour traiter une réponse d'un réseau, utilisez weak, car elles ont une longue durée de vie. Le contrôleur de vue peut se fermer avant la fin de la demande pour que self ne pointe plus vers un objet valide lors de l'appel de la fermeture.
  • Si vous avez une fermeture qui gère un événement sur un bouton. Cela peut être unowned car dès que le contrôleur de vue s'en va, le bouton et tous les autres éléments auxquels il fait référence depuis self disparaissent en même temps. Le bloc de fermeture disparaîtra également au même moment.

    class MyViewController: UIViewController {
          @IBOutlet weak var myButton: UIButton!
          let networkManager = NetworkManager()
          let buttonPressClosure: () -> Void // closure must be held in this class. 
    
          override func viewDidLoad() {
              // use unowned here
              buttonPressClosure = { [unowned self] in
                  self.changeDisplayViewMode() // won't happen after vc closes. 
              }
              // use weak here
              networkManager.fetch(query: query) { [weak self] (results, error) in
                  self?.updateUI() // could be called any time after vc closes
              }
          }
          @IBAction func buttonPress(self: Any) {
             buttonPressClosure()
          }
    
          // rest of class below.
     }
    
84
possen

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é] .

La documentation de Apple Swift contient une excellente section d’images illustrant la différence entre l’utilisation de strong , faible et sans propriétaire dans les fermetures:

https://developer.Apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html

67
TenaciousJay

Voici de brillantes citations de Forums des développeurs Apple décris de délicieux détails:

unowned vs unowned(safe) vs unowned(unsafe)

unowned(safe) est une référence non propriétaire qui affirme sur l'accès que l'objet est toujours vivant. C'est un peu comme une référence facultative faible qui est implicitement déballée avec x! à chaque accès. unowned(unsafe) est semblable à __unsafe_unretained dans ARC. Il s'agit d'une référence non propriétaire, mais rien au moment de l'exécution ne vérifie que l'objet est toujours actif, les références en suspens vont donc aller dans la mémoire erronée. unowned est toujours synonyme de unowned(safe) actuellement, mais l'objectif est qu'il soit optimisé pour unowned(unsafe) dans les constructions -Ofast lorsque les vérifications à l'exécution sont désactivées.

unowned vs weak

unowned utilise en réalité une implémentation beaucoup plus simple que weak. Les objets natifs Swift portent deux comptes de référence et unowned références renverse le décompte de références (---) au lieu du fort compte de références . L'objet est désinitialisé lorsque son compte de références fort atteint zéro, mais il n'est pas réellement désalloué jusqu'à ce que le décompte de références non possédées atteint également zéro. Cela entraîne un léger allongement de la mémoire lorsqu'il y a des références non possédées, mais cela ne pose généralement pas de problème lorsque unowned est utilisé car les objets associés doivent avoir une durée de vie presque égale, et ils sont beaucoup plus simples et plus bas. -overhead que l'implémentation basée sur la table latérale utilisée pour la remise à zéro des références faibles.

Mise à jour: Dans les modernes Swift weak utilise en interne le même mécanisme que unowned fait . Cette comparaison est donc incorrecte car elle compare Objective-C weak à Swift unonwed.

Les raisons

Quel est le but de garder la mémoire vivante après que les références possédées atteignent 0? Que se passe-t-il si le code tente de faire quelque chose avec l'objet en utilisant une référence non possédée après sa désinitialisation?

La mémoire est maintenue en vie afin que ses comptes de conservation soient toujours disponibles. Ainsi, lorsque quelqu'un tente de conserver une référence forte à l'objet sans propriétaire, le moteur d'exécution peut vérifier que le nombre de références fortes est supérieur à zéro afin de garantir la sécurité de la conservation de l'objet.

Qu'advient-il de la possession ou de la non-possession de références détenues par l'objet? Leur durée de vie est-elle découplée de l'objet lorsqu'il est désinitialisé ou leur mémoire est-elle également conservée jusqu'à la désaffectation de l'objet après la libération de la dernière référence non possédée?

Toutes les ressources appartenant à l'objet sont libérées dès que la dernière référence forte de l'objet est publiée et que sa définition est exécutée. Les références non possédées ne font que garder la mémoire vivante - mis à part l'en-tête avec le nombre de références, son contenu est indésirable.

Excité, hein?

48
Valentin Shergin

Il y a quelques bonnes réponses ici. Mais les récents changements apportés à la manière dont Swift implémente des références faibles devraient modifier les décisions d'auto-utilisation de chacun contre le moi faible. Auparavant, si vous aviez besoin de la meilleure performance, utiliser le moi non possédé était supérieur au soi faible, tant que vous pouviez être sûr que le moi ne serait jamais nul, car accéder à un soi sans propriétaire est beaucoup plus rapide qu’un accès à un soi faible.

Mais Mike Ash a documenté comment Swift a mis à jour la mise en œuvre de vars faibles pour utiliser des tables latérales et comment cela améliore considérablement les performances faibles.

https://mikeash.com/pyblog/friday-qa-2017-09-22-Swift-4-weak-references.html

Maintenant qu’il n’ya pas de pénalité significative en termes de performance pour un moi faible, je pense que nous devrions l’utiliser par la suite. L'avantage de l'individu faible est que c'est facultatif, ce qui facilite beaucoup l'écriture d'un code plus correct. C'est essentiellement la raison pour laquelle Swift est un si bon langage. Vous pensez peut-être que vous savez quelles situations sont sans danger pour l’utilisation de personnes sans propriétaire, mais mon expérience de la révision de nombreux codes de développeurs est, pour la plupart, non. J'ai corrigé un grand nombre de collisions dans lesquelles un individu non propriétaire était désalloué, généralement dans les situations où un thread d'arrière-plan se termine après la désallocation d'un contrôleur.

Les bogues et les accidents sont les parties de la programmation les plus longues, les plus douloureuses et les plus coûteuses. Faites de votre mieux pour écrire le code correct et les éviter. Je recommande d'en faire une règle pour ne jamais forcer les options non emballées et ne jamais utiliser un soi non possédé au lieu d'un soi faible. Vous ne perdrez rien de ce qui vous manque dans les moments où la force doit se dérouler et où le non-propriétaire est en sécurité. Mais vous aurez beaucoup à gagner en éliminant les bogues et les bugs difficiles à trouver et à déboguer.

33
SafeFastExpressive

Selon Apple-doc

  • Les références faibles sont toujours de type facultatif et deviennent automatiquement nulles lorsque l'instance à laquelle elles font référence est désallouée.

  • Si la référence capturée ne devient jamais nulle, elle doit toujours être capturée comme une référence non possédée, plutôt que comme une référence faible.

Exemple -

    // if my response can nil use  [weak self]
      resource.request().onComplete { [weak self] response in
      guard let strongSelf = self else {
        return
      }
      let model = strongSelf.updateModel(response)
      strongSelf.updateUI(model)
     }

    // Only use [unowned self] unowned if guarantees that response never nil  
      resource.request().onComplete { [unowned self] response in
      let model = self.updateModel(response)
      self.updateUI(model)
     }
3
Jack

Si rien de ce qui précède n'a de sens:

tl; dr

Tout comme un implicitly unwrapped optional, si vous pouvez garantir que la référence ne sera pas nulle à son point d'utilisation, utilisez non propriétaire. Si non, alors vous devriez utiliser faible.

Explication:

J'ai récupéré ce qui suit ci-dessous à: lien faible non possédé . D'après ce que j'ai compris, le moi non possédé ne peut pas être nul, mais le moi faible peut l'être, et le moi non possédé peut conduire à des pointeurs en suspens ... quelque chose d'infâme Objectif c. J'espère que ça aide

"UNOWNED Les références faibles et non possédées se comportent de la même manière mais ne sont PAS identiques."

Les références non possédées, comme les références faibles, n'augmentent pas le nombre de conservations de l'objet référencé. Toutefois, dans Swift, une référence non possédée présente l’avantage supplémentaire de ne pas être une option . Cela les rend plus faciles à gérer plutôt que de recourir à la liaison facultative. Ce n'est pas sans rappeler les options implicitement non emballées. De plus, les références non possédées sont non mises à 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 possédées peut, dans certains cas, conduisent à des pointeurs en suspens . Pour les nerds qui se souviennent des jours Objective-C comme moi, les références non possédées correspondent à des références unsafe_unretained.

C'est là que ça devient un peu déroutant.

Les références faibles et non possédées n'augmentent pas le nombre de retenues.

Ils peuvent tous deux être utilisés pour rompre les cycles de rétention. Alors, quand les utilisons-nous?!

Selon la documentation d'Apple :

“Utilisez une référence faible chaque fois qu'elle 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 quand vous savez que la référence ne sera jamais nulle une fois qu'elle aura été définie lors de l'initialisation. ”

0
Dennis

Cycles de référence puissants pour les fermetures

Un cycle de référence fort peut également se produire si vous affectez une fermeture à une propriété d'une instance de classe et si le corps de cette fermeture capture l'instance. Cette capture peut se produire parce que le corps de la fermeture accède à une propriété de l’instance, telle que self.someProperty, ou parce que la fermeture appelle une méthode sur l’instance, telle que self.someMethod(). Dans les deux cas, ces accès provoquent la "capture" self de la fermeture, créant ainsi un cycle de référence puissant. Pour plus d'informations sur capture des valeurs dans une fermeture

Swift fournit une solution élégante à ce problème, appelée liste de capture de fermeture . Une liste de capture définit les règles à utiliser pour capturer un ou plusieurs types de référence dans le corps de la fermeture. Comme pour les cycles de référence forts entre deux instances de classe, vous déclarez que chaque référence capturée est une référence weak ou unowned plutôt qu'une référence strong. Le choix approprié de weak ou unowned dépend des relations entre les différentes parties de votre code. plus ici SO

  • Utilisez une référence weak chaque fois qu'il est valide pour que cette référence devienne nil à un moment donné de son existence.
  • Utilisez une référence unowned lorsque vous savez que la référence ne sera jamais nil une fois qu'elle a été définie lors de l'initialisation.

Doc avec exemple

0
yoAlex5