web-dev-qa-db-fra.com

Où va le moi faible?

Je le fais souvent,

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   beep()
}

et dans une application, nous le faisons souvent

tickle.fresh(){
    msg in
    Paint()
}

mais si vous faites this

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   tickle.fresh(){
      msg in
      Paint()
   }
}

bien sûr vous devez faire this

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) { [weak self] _ in
   tickle.fresh(){
      msg in
      self?.Paint()
   }
}

ou peut-être this

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) {
   tickle.fresh(){
      [weak self] msg in
      self?.Paint()
   }
}

ou peut-être ceci

let when = DispatchTime.now() + 2.0
DispatchQueue.main.asyncAfter(deadline: when) { [weak self] _ in
   tickle.fresh(){
      [weak self] msg in
      self?.Paint()
   }
}

W T H devrions-nous faire?

Les trois suggestions semblent fonctionnent parfaitement. Quelle est la profondeur de la signification ici? Et que doit-on faire? Est-ce qu'une référence forte à une référence faible, une référence faible ou forte? Être ou ne pas être? C'est la question!

39
Fattie

Tout d'abord, notez que vous n'avez généralement pas à vous soucier de la conservation des cycles avec DispatchQueue.main.asyncAfter, car la fermeture sera exécutée à quelque point . Par conséquent, que vous capturiez ou non faiblement self, vous ne créerez pas de cycle de conservation permanent (en supposant que tickle.fresh ne le fait pas non plus.

Que vous mettiez ou non un [weak self] La liste de capture sur la clôture asyncAfter extérieure dépend entièrement de la décision de conserver self jusqu'à ce que la fermeture soit appelée (après le délai que vous avez défini). Si vous n'avez pas besoin de self pour rester en vie jusqu'à ce que la fermeture soit appelée, mettez [weak self] in, si vous le faites, alors ne le mettez pas.

Que vous mettiez ou non un [weak self] sur la fermeture intérieure (celle passée à tickle.fresh) dépend si vous avez déjà faiblement capturé self dans la fermeture extérieure. Si vous ne l'avez pas encore fait, vous pouvez mettre [weak self] afin d'éviter que la fermeture intérieure ne la retienne. Si toutefois, la fermeture extérieure a déjà été faiblement capturée self, alors la fermeture intérieure aura déjà une référence faible à self, ajoutant ainsi [weak self] à la fermeture intérieure n'aura aucun effet.

Donc, pour résumer:


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
   tickle.fresh { msg in
      self.Paint()
   }
}

self sera retenu à la fois par la fermeture extérieure et intérieure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   tickle.fresh { msg in
      self?.Paint()
   }
}

self ne sera pas retenu par les deux fermetures.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   tickle.fresh { [weak self] msg in
      self?.Paint()
   }
}

Comme ci-dessus, le [weak self] car la fermeture intérieure n'a aucun effet, car self est déjà faiblement capturé par la fermeture extérieure.


DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
   tickle.fresh { [weak self] msg in
      self?.Paint()
   }
}

self sera retenu par la fermeture extérieure, mais pas par la fermeture intérieure.


Bien sûr, il se peut que vous ne vouliez pas que self soit retenu par la fermeture extérieure, mais vous faites être retenu par la fermeture intérieure. Dans de tels cas, vous pouvez déclarer une variable locale dans la fermeture externe afin de conserver une référence forte à self, lorsque vous pourrez alors capturer dans la fermeture interne:

DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
   guard let strongSelf = self else { return }
   tickle.fresh { msg in
      strongSelf.Paint()
   }
}

Maintenant, self ne sera pas maintenu en vie par la fermeture externe, mais une fois appelé, si self existera toujours, il sera maintenu en vie par la fermeture interne jusqu'à ce que la fermeture soit désallouée.


En réponse à:

Est-ce qu'une référence forte à une référence faible, une référence faible ou forte?

Les références faibles sont implémentées en tant qu'options, qui sont des types de valeur. Par conséquent, vous ne pouvez pas directement directement - vous devez d'abord le décompresser, puis prendre une référence forte à l'instance sous-jacente. Dans ce cas, vous avez simplement affaire à une référence forte (exactement comme dans l'exemple ci-dessus avec strongSelf).

Cependant, si une référence faible est encadrée (cela se produit avec la capture de fermeture - le type de valeur sera placé dans une zone allouée au segment de mémoire) - vous pouvez alors en effet avoir une forte référence à cette case. L'effet de ceci est équivalent à une référence faible à l'instance d'origine, vous avez juste un peu invisible d'indirection supplémentaire.

En fait, c’est exactement ce qui se passe dans l’exemple où la fermeture externe capture faiblement self et la fermeture interne 'capture fortement' qui référence faible. L'effet est qu'aucune fermeture ne retient self.

102
Hamish