Utilisation du self faible dans la fonction dispatch_async

J'ai lu beaucoup de messages sur l'utilisation de __weak self à __weak self intérieur de dispatch_async , et maintenant je suis un peu confus.

si j'ai :

 self.myQueue = dispatch_queue_create("com.biview.core_data", NULL); dispatch_async(self.myQueue, ^(void){ if (!self.var1) { self.var1 = ...; } dispatch_async(dispatch_get_main_queue(), ^(void) { if ([self.var2 superview]) { [self.var2 removeFromSuperview]; } [self.Label setText:text]; }); }); 

dois-je utiliser __weak self . Parce que j'ai lu que dans certains cas, dispatch_async n'a pas besoin de __weak self .

Voir dernier commentaire ici

En supposant, self est un pointeur d'object vers un UIViewController .

Choses à considérer:

  • Un UIViewController est un object "UIKit". Les objects UIKit ne doivent pas être envoyés sur des threads non-principaux, c'est-à-dire que ces methods ne doivent s'exécuter que sur le thread principal!

  • Un bloc qui a été mis en queue dans une queue – que ce soit de manière synchrone ou asynchronous – sera éventuellement exécuté – quoi qu'il arrive! Eh bien, à less que le programme se termine avant que cela puisse arriver.

  • Les pointeurs forts retenus capturés seront conservés lorsque le bloc sera copié (par exemple, lors de l'envoi asynchronous), et à nouveau libérés lorsque le bloc sera détruit (après l'avoir terminé).

  • Les pointeurs faibles retenus capturés ne seront PAS conservés et ne seront pas relâchés.

Dans votre scénario, lorsque vous capturez vous- même dans le bloc qui est dissortingbué dans la queue principale , vous n'avez pas besoin de vous inquiéter que de mauvaises choses se produisent.

Alors pourquoi? Et que se passe-t-il réellement?

Puisque self sera capturé dans le bloc qui est envoyé de manière asynchronous , self sera implicitement conservé , et relâché à nouveau lorsque le bloc sera terminé.

Cela signifie que la vie de soi sera prolongée jusqu'à la fin du bloc. Notez que votre deuxième bloc est dissortingbué sur le thread principal , et il est garanti que self est toujours en vie lorsque ce bloc est exécuté.

Cette "vie prolongée" ci-dessus, pourrait être une caractéristique désirée de votre programme.

Si vous ne voulez pas explicitement prolonger la durée de vie de l'object UIViewController , et que vous voulez que le bloc – lorsqu'il est finalement exécuté – vérifie si cet object UIViewController existe encore, vous pouvez utiliser un pointeur __weak de self. Notez que le bloc est finalement exécuté, peu importe si UIViewController est toujours UIViewController ou a été désalloué entre-time.

Vous pourriez vouloir que le bloc fasse "rien" si le UIViewController a été UIViewController avant que le bloc ne soit exécuté:

 MyController* __weak weakSelf = self; dispatch_async(queue, ^{ MyController* strongSelf = weakSelf; if (strongSelf) { ... } else { // self has been deallocated in the meantime. } }); 

Voir aussi: Transition vers les notes de publication ARC

Rappelez-vous: les objects UIKit ne doivent pas être envoyés sur des threads non-principaux!

Une autre erreur subtile peut se produire au fait que les objects UIKit doivent exécuter des methods uniquement sur le thread principal.

Cela peut être violé si un bloc capture un object UIKit dissortingbué de manière asynchronous et s'exécute sur un thread non principal . Il peut alors arriver que le bloc contienne la dernière reference forte à cet object UIKit . Maintenant, lorsque le bloc sera finalement exécuté, le bloc sera détruit et l'object UIKit sera libéré. Comme il s'agit de la dernière reference forte à l'object UIKit , il sera désalloué. Cependant, cela se produit sur le thread où le bloc a été exécuté – et ce n'est pas le thread principal! Maintenant, de mauvaises choses peuvent arriver (et arriveront généralement), puisque la méthode dealloc est toujours une méthode envoyée à un object UIKit .

Vous pouvez éviter cette erreur en envoyant un bloc capturant un pointeur fort vers cet object UIKit et lui envoyer une méthode factice:

 UIViewController* strongUIKitPointer = ... dispatch_async(non_main_queue, ^{ ... // do something dispatch(dispatch_get_main_queue(), ^{ [strongUIKitPointer self]; // note: self is a method, too - doing nothing }); }); 

Dans votre scénario cependant, la dernière reference forte pourrait être seulement dans le bloc qui s'exécute sur le thread principal. Donc, vous êtes à l'abri de cette erreur subtile. 😉

Modifier:

Dans votre configuration, vous n'avez jamais de cycle de conservation. Un cycle de rétention se produit si un object retenant A fait fortement reference à un autre object retenant B, et l'object B fait fortement reference à A. Notez qu'un "bloc" est également un object retenant.

Un exemple artificiel avec une reference cyclique:

 typedef void(^my_completion_block_t)(NSArray* result); @interface UsersViewController : UIViewController @property (nonatomic, copy) my_completion_block_t completion; @property (nonatomic) NSArray* users; @end 

Ici, nous avons une complétion de propriété dont le type de valeur est un bloc. C'est-à-dire, nous obtenons un ivar avec le nom _completion dont le type est un bloc.

Un client peut définir un gestionnaire d'achèvement qui doit être appelé lorsqu'une certaine opération est terminée. Supposons que l'opération extrait une list d'users à partir d'un server distant. Le plan consiste à définir les users de la propriété une fois l'opération terminée:

L'approche imprudente introduirait accidentellement une reference cyclique:

Quelque part dans "UsersViewController.m"

 self.completion = ^(NSArray* users){ self.users = users; } [self fetchUsers]; // start asynchronous task 

Ici, self contient une reference forte à la _completion ivar, qui est un bloc. Et le bloc lui-même capture le soi , ce qui provoque de se retenir quand le bloc est copié quand il est dissortingbué. C'est un cycle de reference classique.

Afin d'éviter cette reference cyclique, nous avons quelques alternatives:

  1. Utiliser un pointeur qualifié de __weak

     UsersViewController* __weak weakSelf = self; self.completion = ^(NSArray* users) { UsersViewController* strongSelf = weakSelf; if (strongSelf) { strongSelf.users = users; } else { // the view controller does not exist anymore } } [usersViewController fetchUsers]; 
  2. Utilisation d'un pointeur __block qualifié de self et définissant finalement nil dans le bloc quand il se termine:

     UsersViewController* __block blockSelf = self; self.completion = ^(NSArray* users) { blockSelf.users = users; blockSelf = nil; } [usersViewController fetchUsers]; 

Voir aussi: Transition vers les notes de publication ARC

Mise à jour rapide:

Un exemple de cette danse dite forte-faible dans swift:

 func doSomeThingAsynchronously() { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> () in // Do task in default queue dispatch_async(dispatch_get_main_queue(), { [weak self] () -> () in guard let strongSelf = self else { return } // Do task in main queue strongSelf.updateView() }) } } 

Projet open source populaire Alamofire utilise cette approche.

Prolongez la durée de vie de l'object en utilisant le [self faible] et gardez let strongSelf = self else {return} idiome.

Pour plus d'informations, consultez le guide de style rapide

Swift 3 mise à jour:

 func doSomeThingAsynchronously() { DispatchQueue.global().async { // Do task in default queue DispatchQueue.main.async { [weak self] in // Do task in main queue guard let strongSelf = self else { return } strongSelf.updateView() } } }