web-dev-qa-db-fra.com

Utilisation de __block et __weak

J'ai lu ce fil: Que signifie le mot clé "__block"? qui explique ce que __block est utilisé pour mais je suis confus au sujet d'une des réponses. Ça dit __block est utilisé pour éviter de conserver les cycles, mais les commentaires en dessous me laissent incertain.

Je l'utilise quelque chose comme ça:

 self.someProperty = x; //where x is some object (id)
 __block __weak VP_User *this = self;

 //begin a callback-style block
     this.someProperty = nil;

Dois-je utiliser les deux __block et __weak? Des problèmes flagrants avec cette façon dont cela ressemble?

27
Adam

__block est un qualificatif de stockage. Il spécifie que la variable doit être directement capturée par le bloc au lieu de la copier. Ceci est utile dans le cas où vous devez modifier la variable d'origine, comme dans l'exemple suivant

__block NSString *aString = @"Hey!"; 
void(^aBlock)() = ^{ aString = @"Hello!" }; // without __block you couldn't modify aString
NSLog(@"%@", aString); // Hey!
aBlock();
NSLog(@"%@", aString); // Hello!

Dans ARC, la variable est automatiquement conservée, de sorte qu'elle peut être référencée en toute sécurité dans l'implémentation du bloc. Dans l'exemple précédent, aString reçoit alors un message retain lorsqu'il est capturé dans le contexte du bloc.

Non pas que cela ne soit pas vrai dans MRC (Manual Reference Counting) où la variable est référencée sans être conservée.

Marquage comme __weak fait que la variable n'est pas conservée, donc le bloc s'y réfère directement mais sans la conserver. Ceci est potentiellement dangereux car dans le cas où le bloc vit plus longtemps que la variable, car il fera référence à la mémoire poubelle (et risque de planter).

Voici le paragraphe pertinent du doc clang :

Dans les langages Objective-C et Objective-C++, nous autorisons le __weak spécificateur pour __block variables de type objet. [...] Ce qualificatif permet de conserver ces variables sans envoyer de messages de conservation. Cela conduit sciemment à pointeurs pendants si le bloc (ou une copie) survit à la durée de vie de cet objet.

Enfin, l'affirmation selon laquelle __block peut être utilisé pour éviter des cycles de référence forts (aka retenir les cycles) est tout à fait faux dans un contexte ARC. Étant donné que dans ARC __block fait en sorte que la variable soit fortement référencée, elle est en fait plus susceptible de les provoquer.

Par exemple, dans MRC, ce code rompt un cycle de conservation

__block typeof(self) blockSelf = self; //this would retain self in ARC!
[self methodThatTakesABlock:^ {
    [blockSelf doSomething];
}];

alors que pour obtenir le même résultat dans ARC, vous faites normalement

__weak typeof(self) weakSelf = self;
[self methodThatTakesABlock:^ {
    [weakSelf doSomething];
}];
60

Tu devrais utiliser __block si vous souhaitez modifier la valeur de la variable dans le bloc.

par exemple:

__block BOOL result = NO;
dispatch_sync(dispatch_get_main_queue(), ^{
  ...
  result = YES;
  ...
});

Tu devrais utiliser __weak si vous voulez éviter de conserver les cycles.

par exemple.:

__weak typeof(self) wself = self;
self.foobarCompletion = ^{
  ...
  wself.foo = YES;
  ...
};

Vous pouvez les combiner en cas de besoin.

14