5 votes

Modification des données de la portée parent dans une file d'attente de distribution

Selon le guide de la programmation en concomitance :

Lorsque des blocs sont ajoutés à une file d'attente de distribution, ces valeurs doivent généralement être laissées en lecture seule. Toutefois, les blocs qui sont exécutés de manière synchrone peuvent également utiliser des variables auxquelles le mot-clé __block a été ajouté pour renvoyer des données à la portée d'appel du parent.

J'ai modifié des variables créées en dehors de la file d'attente, mais je ne les ai jamais spécifiées avec __block Je me demande donc quand et pourquoi cela est nécessaire. Ou bien est-ce que les variables d'instance sont toujours intrinsèquement modifiables par les blocs, comme si elles avaient __block qui leur sont attribuées en coulisses ?

Mise à jour : Je dois aussi ajouter que j'utilise des files d'attente asynchrones, alors que le texte ci-dessus indique que les variables ne peuvent être modifiées que dans les files d'attente synchrones (avec __block )

2voto

Martin R Points 105727

Accès à une variable d'instance iVar de votre classe à l'intérieur d'un bloc est interprété par le compilateur comme self->iVar . Par conséquent, le bloc capture self qui n'est pas modifié.

Je suis sûr que le __block fonctionne également avec dispatch_async Il s'agit donc d'une erreur de documentation.

ADDED

L'exemple suivant montre comment une __block peut être utilisée avec dispatch_async :

dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
__block int total = 0;
printf("address of total: %p\n", &total);

// dispatch some (concurrent) blocks asynchronously:
dispatch_async(queue, ^{
    OSAtomicAdd32(5, &total);
});
dispatch_async(queue, ^{
    OSAtomicAdd32(7, &total);
});

// Wait for all blocks to complete:
dispatch_barrier_sync(queue, ^{ });

printf("address of total: %p\n", &total);
printf("total=%d\n", total);

Sortie :

address of total: 0x7fff5fbff8f0
address of total: 0x100108198
total=12

On peut voir que total est copié de la pile au tas lorsque les blocs sont exécutés.

ADDED

Je viens de trouver ceci dans le Guide de programmation des blocs . Il explique pourquoi il n'y a aucun problème à utiliser __block variables avec des blocs asynchrones.

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

En guise d'optimisation, le stockage en bloc commence sur le pile, tout comme les blocs eux-mêmes. Si le bloc est copié en utilisant Block_copy (ou en Objective-C lorsque le bloc est envoyé en copie), les variables sont copiées sur le tas. Ainsi, l'adresse d'une variable __block peut changer au fil du temps.

2voto

Johnnywho Points 3058

La citation du Concurrency Programming Guide concerne les variables qui sont créées à l'intérieur d'une fonction ou d'une méthode. Les variables créées à l'intérieur de ces dernières sont créées sur le pile qui les conserve aussi longtemps que l'appel de fonction. En d'autres termes, lorsque la fonction revient, son cadre de pile est détruit en même temps que les variables.

Les objets en Objective-c sont créés sur le amas pour qu'ils puissent vivre après les retours de fonction, cependant quand vous créez une ligne comme :

MyClass *object = [[MyClass alloc] init];

Vous créez un objet qui sera placé sur le tas, mais vous créez aussi une variable object qui contient le pointeur vers votre objet dans le tas. Cette variable est placée sur le cadre de la pile de la méthode/fonction actuelle et sera éliminée après son retour. Votre objet, si vous ne le libérez pas, ne sera pas éliminé même si la fonction est terminée.

C'est pourquoi les blocs copient les variables de la portée du parent qui sont référencées à l'intérieur du bloc. Elles sont copiées dans la mémoire privée du bloc parce qu'elles pourraient être détruites à la fin de l'appel de la fonction, et comme vous le savez, le bloc pourrait être utilisé après cela. C'est pourquoi vous devez utiliser __block pour informer le bloc de ne pas copier la variable de la pile et de l'utiliser directement. Comme ces variables peuvent être détruites au retour de la fonction, le Concurrency Programming Guide indique que seule la répartition synchrone doit être utilisée dans ce cas, car elle garantit que le bloc est exécuté avant le retour de la fonction. EDIT : Comme Martin R l'a correctement souligné __block Les variables ne sont pas toujours utilisées directement depuis la pile. Comme cette documentation dit, ils sont traités comme des pointeurs et leur adresse peut être modifiée depuis la pile lorsque le bloc est copié (comme dans le dispatch). Les variables copiées sont placées dans un stockage qui est partagé entre les blocs et la fonction pour prolonger la durée de vie de la variable. Je pense que la raison pour laquelle il est indiqué que les variables locales doivent être utilisées avec dispatch_sync est que dans les environnements non-ARC, les variables ne sont pas conservées. * et créer ainsi un risque d'utilisation d'un objet désalloué. Pour la même raison, je pense qu'il ne faut pas utiliser __block devant les variables statiques et d'instance.

Les variables qui sont variables d'instance o variables statiques ne vivent pas sur la pile d'une fonction, ils ne sont donc pas copiés par bloc, et vous n'avez pas besoin de __block spécificateur. Les variables statiques vivent tant que le programme est exécuté, mais les variables d'instance sont détruites lorsque l'objet qui les contient est détruit. C'est pourquoi les blocs ont une autre caractéristique - tous les objets qui sont référencés à l'intérieur d'un bloc seront conservés pendant toute la durée de vie du bloc afin de garantir que l'objet ne sera pas désalloué de manière inattendue.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X