Que signifie le mot clé "__block"?

446

Que signifie exactement le __blockmot - clé dans Objective-C? Je sais que cela vous permet de modifier des variables dans des blocs, mais j'aimerais savoir ...

  1. Que dit-il exactement au compilateur?
  2. Cela fait-il autre chose?
  3. Si c'est tout ce qu'il fait, pourquoi est-il nécessaire en premier lieu?
  4. Est-ce dans les documents n'importe où? (Je ne le trouve pas).
mjisrawi
la source
3
vérifiez ici , et la section "Blocs et variables".
1
@Code Monkey: Je posais des questions spécifiques sur le mot-clé, pas sur la syntaxe en général. Alors ne pensez pas que c'est vraiment un doublon.
mjisrawi
3
@Code Monkey: Non, ce n'est pas un doublon. La question que vous mentionnez ne parle pas __blockdu tout.
DarkDust
3
Et si quelqu'un se demande comment les Objective-C __blockdevraient se traduire en Swift: «Les fermetures [dans Swift] ont une sémantique de capture similaire à celle des blocs [dans Objective-C] mais diffèrent d'une manière clé: les variables sont mutables plutôt que copiées. En d'autres termes, le comportement de __block dans Objective-C est le comportement par défaut des variables dans Swift. » Tiré du livre d'Apple: Utilisation de Swift avec Cocoa et Objective-C (Swift 2.2).
Jari Keinänen

Réponses:

543

Il indique au compilateur que toute variable marquée par lui doit être traitée d'une manière spéciale lorsqu'elle est utilisée à l'intérieur d'un bloc. Normalement, les variables et leur contenu qui sont également utilisés dans les blocs sont copiés, ainsi toute modification apportée à ces variables n'apparaît pas en dehors du bloc. Quand ils sont marqués avec__block , les modifications effectuées à l'intérieur du bloc sont également visibles à l'extérieur de celui-ci.

Pour un exemple et plus d'informations, consultez Le type de stockage __block dans les rubriques de programmation des blocs d'Apple .

L'exemple important est celui-ci:

extern NSInteger CounterGlobal;
static NSInteger CounterStatic;

{
    NSInteger localCounter = 42;
    __block char localCharacter;

    void (^aBlock)(void) = ^(void) {
        ++CounterGlobal;
        ++CounterStatic;
        CounterGlobal = localCounter; // localCounter fixed at block creation
        localCharacter = 'a'; // sets localCharacter in enclosing scope
    };

    ++localCounter; // unseen by the block
    localCharacter = 'b';

    aBlock(); // execute the block
    // localCharacter now 'a'
}

Dans cet exemple, les deux localCounteret localCharactersont modifiés avant l'appel du bloc. Cependant, à l'intérieur du bloc, seule la modification à localCharacterserait visible, grâce au __blockmot - clé. Inversement, le bloc peut être modifié localCharacteret cette modification est visible à l'extérieur du bloc.

DarkDust
la source
8
Excellente explication concise et un exemple très utile. Merci!
Evan Stone
1
Comment aBlock modifie localCounter? Il ne semble que modifier CounterGlobal. Merci
CommaToast
8
Il ne modifie pas localCounter, mais il modifie localCharacter. Faites également attention à la valeur localCounterdu bloc: c'est 42, même si la variable est augmentée avant l'appel du bloc mais après la création du bloc (c'est à ce moment que la valeur a été "capturée").
DarkDust
1
C'est une explication utile - cependant - pouvez-vous expliquer ce qui semble être des déclarations contradictoires dans votre explication? Vous dites ci-dessus que "aBlock modifie ... localCounter" et ensuite dans les commentaires vous dites "[aBlock] ne modifie PAS localCounter". Lequel est-ce? S'il n'est "pas modifié", votre réponse doit-elle être modifiée?
Praxiteles
2
En général, les variables sans __block sont capturées par valeur et compressées dans "l'environnement" du bloc, lorsque le bloc est créé. Mais __block vars ne sera pas capturé, chaque fois qu'ils sont utilisés à l'intérieur ou à l'extérieur d'un bloc, ils sont référencés tels quels.
jchnxu
27

@bbum couvre les blocs en profondeur dans un article de blog et touche au type de stockage __block.

__block est un type de stockage distinct

Tout comme statique, automatique et volatile, __block est un type de stockage. Il indique au compilateur que le stockage de la variable doit être géré différemment.

...

Cependant, pour les variables __block, le bloc n'est pas conservé. C'est à vous de conserver et de libérer, selon les besoins.
...

En ce qui concerne les cas d'utilisation, vous constaterez qu'il __blockest parfois utilisé pour éviter les cycles de conservation car il ne conserve pas l'argument. Un exemple courant est d'utiliser soi-même.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;
Joe
la source
Voir cet article pour plus d'informations sur le problème du cycle de rétention: benscheirman.com/2012/01/… . Cela __weaksuffirait-il également dans ce cas précis? C'est peut-être un peu plus clair ...
Hari Karam Singh
17
Enfin, l'affirmation selon laquelle __block peut être utilisé pour éviter les cycles de référence forts (aka conserver les cycles) est tout à fait erronée dans un contexte ARC. Étant donné que dans ARC __block, la variable est fortement référencée, il est en fait plus probable de les provoquer. stackoverflow.com/a/19228179/189006
Krishnan
10

Normalement, lorsque vous n'utilisez pas __block, le bloc copiera (conservera) la variable, donc même si vous modifiez la variable, le bloc a accès à l'ancien objet.

NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints @"hello"

Dans ces 2 cas, vous avez besoin de __block:

1.Si vous souhaitez modifier la variable à l'intérieur du bloc et vous attendre à ce qu'elle soit visible à l'extérieur:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    str = @"how are you";
};
theBlock();
NSLog(@"%@", str); //prints "how are you"

2.Si vous souhaitez modifier la variable après avoir déclaré le bloc et que vous vous attendez à ce que le bloc voit le changement:

__block NSString* str = @"hello";
void (^theBlock)() = ^void() {
    NSLog(@"%@", str);
};
str = @"how are you";
theBlock(); //prints "how are you"
Hamid Vakilian
la source
8

__block est un qualificatif de stockage qui peut être utilisé de deux manières:

  1. Marque qu'une variable vit dans un stockage partagé entre la portée lexicale de la variable d'origine et tous les blocs déclarés dans cette portée. Et clang générera une structure pour représenter cette variable et utilisera cette structure par référence (et non par valeur).

  2. Dans MRC, __block peut être utilisé pour éviter de conserver les variables d'objet qu'un bloc capture. Attention à ce que cela ne fonctionne pas pour ARC. Dans ARC, vous devez utiliser __weak à la place.

Vous pouvez vous référer à Apple Doc pour des informations détaillées.

Mindy
la source
6

__blockest un type de stockage utilisé pour rendre les variables de portée mutables, plus franchement si vous déclarez une variable avec ce spécificateur, sa référence sera transmise aux blocs et non à une copie en lecture seule pour plus de détails, voir Programmation des blocs dans iOS

mithilesh
la source
2

J'espère que cela vous aidera

supposons que nous ayons un code comme:

{
     int stackVariable = 1;

     blockName = ^()
     {
      stackVariable++;
     }
}

cela donnera une erreur comme "la variable n'est pas assignable" car la variable de pile à l'intérieur du bloc est par défaut immuable.

l'ajout de __block (modificateur de stockage) avant sa déclaration le rend modifiable à l'intérieur du bloc, c'est-à-dire __block int stackVariable=1;

Anurag Bhakuni
la source
1

De la spécification de langue de bloc :

En plus du nouveau type de bloc, nous introduisons également un nouveau qualificatif de stockage, __block, pour les variables locales. [testme: une déclaration __block dans un littéral de bloc] Le qualificatif de stockage __block est mutuellement exclusif aux qualificatifs de stockage local existants auto, register et static. [testme] Les variables qualifiées par __block agissent comme si elles étaient dans le stockage alloué et ce stockage est récupéré automatiquement après la dernière utilisation de ladite variable. Une implémentation peut choisir une optimisation dans laquelle le stockage est initialement automatique et uniquement "déplacé" vers le stockage alloué (tas) lors d'une copie de bloc d'un bloc de référence. Ces variables peuvent être mutées comme le sont les variables normales.

Dans le cas où une variable __block est un bloc, il faut supposer que la variable __block réside dans le stockage alloué et, en tant que telle, est supposée référencer un bloc qui se trouve également dans le stockage alloué (c'est le résultat d'une opération Block_copy). Malgré cela, il n'y a aucune disposition pour faire un Block_copy ou un Block_release si une implémentation fournit un stockage automatique initial pour les blocs. Cela est dû à la condition de concurrence inhérente de potentiellement plusieurs threads essayant de mettre à jour la variable partagée et au besoin de synchronisation autour de l'élimination des anciennes valeurs et de la copie de nouvelles. Une telle synchronisation dépasse le cadre de cette spécification de langage.

Pour plus d'informations sur la compilation d'une variable __block, reportez-vous à la Spécification d'implémentation de bloc , section 2.3.

Martin Gordon
la source
Vos liens sont morts tous les deux
Ben Leggiero
Ce n'est pas vraiment une réponse et pourrait être étoffé ou supprimé. Merci!
Dan Rosenstark
0

Cela signifie que la variable dont il s'agit d'un préfixe est disponible pour être utilisée dans un bloc.

Rich Allen
la source