web-dev-qa-db-fra.com

Quelle est la différence entre une référence __weak et une référence __block?

Je lis la documentation de Xcode, et voici quelque chose qui me laisse perplexe:

__block typeof(self) tmpSelf = self;
[self methodThatTakesABlock:^ {
    [tmpSelf doSomething];
}];

Ce qui suit est copié de la documentation:

Un bloc constitue une référence forte aux variables qu'il capture. Si vous utilisez self dans un bloc, le bloc forme une référence forte à self, donc si self a également une référence forte au bloc (ce qu'il fait généralement), un fort cycle de référence en résulte. Pour éviter le cycle, vous devez créer un faible (ou __block) référence à soi en dehors du bloc, comme dans l'exemple ci-dessus.

Je ne comprends pas ce qui fait un faible (ou __block)' signifier?

Est

__block typeof(self) tmpSelf = self;

et

__weak typeof(self) tmpSelf = self;

exactement la même chose ici?

J'ai trouvé une autre pièce dans le document:

Remarque: dans un environnement récupéré, si vous appliquez les deux __weak et __block modificateurs à une variable, alors le bloc ne s'assurera pas qu'il est maintenu en vie.

Donc, je suis totalement perplexe.

77
HanXu

De la documentation sur __block

Les variables __block vivent dans un stockage partagé entre la portée lexicale de la variable et tous les blocs et copies de bloc déclarés ou créés dans la portée lexicale de la variable. Ainsi, le stockage survivra à la destruction de la trame de pile si des copies des blocs déclarés dans la trame survivent au-delà de la fin de la trame (par exemple, en étant mises en file d'attente quelque part pour une exécution ultérieure). Plusieurs blocs dans une portée lexicale donnée peuvent utiliser simultanément une variable partagée.

De la documentation sur __weak

__weak spécifie une référence qui ne maintient pas vivant l'objet référencé. Une référence faible est définie sur zéro lorsqu'il n'y a pas de références fortes à l'objet.

Ce sont donc des choses techniquement différentes. __block consiste à empêcher la copie de votre variable de votre portée externe dans votre portée de bloc. __weak est un pointeur faible auto-délimitant.

Notez que je l'ai dit techniquement, car pour votre cas, ils feront (presque) la même chose. La seule différence est que vous utilisiez ARC ou non. Si votre projet utilise ARC et est uniquement pour iOS4.3 et supérieur, utilisez __weak. Il garantit que la référence est définie sur nil si la référence de portée globale est libérée d'une manière ou d'une autre. Si votre projet n'utilise pas ARC ou concerne des versions de système d'exploitation plus anciennes, utilisez __block.

Il y a une différence subtile ici, assurez-vous de la comprendre.

EDIT: Une autre pièce du puzzle est __unsafe_unretained. Ce modificateur est presque le même que __weak mais pour les environnements d'exécution antérieurs à 4.3. CEPENDANT, il n'est pas réglé sur zéro et peut vous laisser des pointeurs suspendus.

106
Paul de Lange

En mode de comptage manuel des références, __block id x; a pour effet de ne pas conserver x. En mode ARC, __block id x; par défaut, il conserve x (comme toutes les autres valeurs). Pour obtenir le comportement du mode de comptage manuel des références sous ARC, vous pouvez utiliser __unsafe_unretained __block id x ;. Cependant, comme le nom __unsafe_unretained l'indique, avoir une variable non conservée est dangereux (car il peut osciller) et est donc déconseillé. Deux meilleures options sont soit d'utiliser __weak (si vous n'avez pas besoin de prendre en charge iOS 4 ou OS X v10.6), soit de définir la valeur __block sur nil pour rompre le cycle de conservation.

Documents Apple

5
Andrei Shender

A côté d'autres réponses sur __block contre __weak, il existe un autre moyen d'éviter le cycle de rétention dans votre scénario.

@weakify(self);
[self methodThatTakesABlock:^ {
    @strongify(self);
    [self doSomething];
}];

Plus d'informations sur @Weakify @Strongify Macro

0
Jun Jie Gan

Lorsque vous utilisez self in block, vous devez utiliser __ faible, pas __ block car il peut se conserver.

Dans le cas où vous avez besoin d'un moi fort, vous pouvez utiliser comme ceci:

__weak typeof(self) *weakSelf = self;
[self methodThatTakesABlock:^{
    if (weakSelf) {
        __strong typeof(self) *strongSelf = weakSelf;
        [strongSelf doSomething];
    }
}];
0
david72