Che cosa significa la parola chiave "__block"?


447

Cosa significa esattamente la __blockparola chiave in Objective-C? So che ti permette di modificare le variabili all'interno di blocchi, ma mi piacerebbe sapere ...

  1. Cosa dice esattamente al compilatore?
  2. Fa qualcos'altro?
  3. Se questo è tutto, allora perché è necessario in primo luogo?
  4. È nei documenti da qualche parte? (Non riesco a trovarlo).

3
controlla qui e la sezione "Blocchi e variabili".


1
@Code Monkey: stavo chiedendo in particolare la parola chiave, non la sintassi in generale. Quindi non pensare che sia davvero un duplicato.
mjisrawi,

3
@Code Monkey: No, questo non è un duplicato. La domanda che menzioni non parla __blockaffatto.
DarkDust

3
E se qualcuno si sta chiedendo come gli Objective-C __blockdovrebbero tradursi in Swift: ”Le chiusure [in Swift] hanno una semantica di cattura simile ai blocchi [in Objective-C] ma differiscono in un modo chiave: le variabili sono mutabili piuttosto che copiate. In altre parole, il comportamento di __block in Objective-C è il comportamento predefinito per le variabili in Swift. ” Dal libro di Apple: utilizzo di Swift con Cocoa e Objective-C (Swift 2.2).
Jari Keinänen,

Risposte:


545

Indica al compilatore che qualsiasi variabile contrassegnata da essa deve essere trattata in modo speciale quando viene utilizzata all'interno di un blocco. Normalmente, le variabili e il loro contenuto utilizzato anche nei blocchi vengono copiati, quindi qualsiasi modifica apportata a queste variabili non viene visualizzata all'esterno del blocco. Quando sono contrassegnati con __block, anche le modifiche apportate all'interno del blocco sono visibili all'esterno di esso.

Per un esempio e ulteriori informazioni, vedere Il tipo di archiviazione __block negli argomenti di programmazione dei blocchi Apple .

L'esempio importante è questo:

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'
}

In questo esempio, entrambi localCountere localCharactervengono modificati prima che venga chiamato il blocco. Tuttavia, all'interno del blocco, localCharactersarebbe visibile solo la modifica , grazie alla __blockparola chiave. Viceversa, il blocco può essere modificato localCharactere questa modifica è visibile all'esterno del blocco.


8
Eccellente, concisa spiegazione e un esempio molto utile. Grazie!
Evan Stone,

1
In che modo aBlock modifica localCounter? Sembra solo modificare CounterGlobal. Grazie
CommaToast,

8
Non modifica localCounter, ma modifica localCharacter. Inoltre, presta attenzione al valore localCounterpresente nel blocco: è 42, anche se la variabile viene aumentata prima che il blocco venga chiamato ma dopo che il blocco è stato creato (in quel momento il valore viene "catturato").
DarkDust,

1
Questa è una spiegazione utile - tuttavia - puoi spiegare quali affermazioni contraddittorie sembrano avere nella tua spiegazione? Dici sopra che "aBlock modifica ... localCounter" e poi nei commenti dici "[aBlock] NON modifica localCounter". Cos'è questo? Se è "non modificato", allora la tua risposta dovrebbe essere modificata?
Prassitele

2
In generale, i var senza __block verrebbero catturati per valore e impacchettati nell '"ambiente" del blocco, quando il blocco viene creato. Ma i blocchi __block non verranno catturati, ogni volta che vengono utilizzati all'interno o all'esterno di un blocco, vengono referenziati così come sono.
jchnxu,

28

@bbum copre i blocchi in modo approfondito in un post di blog e tocca il tipo di archiviazione __block.

__block è un tipo di archiviazione distinto

Proprio come statico, automatico e volatile, __block è un tipo di archiviazione. Indica al compilatore che l'archiviazione della variabile deve essere gestita in modo diverso.

...

Tuttavia, per le variabili __block, il blocco non viene mantenuto. Spetta a te conservare e rilasciare, se necessario.
...

Per quanto riguarda i casi d'uso che troverai a __blockvolte viene usato per evitare cicli di trattenimento poiché non mantiene l'argomento. Un esempio comune sta usando sé.

//Now using myself inside a block will not 
//retain the value therefore breaking a
//possible retain cycle.
__block id myself = self;

Vedi questo post per maggiori informazioni sul problema del ciclo di conservazione: benscheirman.com/2012/01/… . Sarebbe __weaksufficiente nel caso specifico come bene? Forse è un po 'più chiaro ...
Hari Karam Singh

17
Infine, l'affermazione secondo cui __block può essere utilizzato per evitare cicli di riferimento forti (ovvero cicli di mantenimento) è chiaramente errata in un contesto ARC. A causa del fatto che in ARC __block si fa fortemente riferimento alla variabile, in realtà è più probabile che li causi. stackoverflow.com/a/19228179/189006
Krishnan

10

Normalmente quando non si usa __block, il blocco copia (mantiene) la variabile, quindi anche se si modifica la variabile, il blocco ha accesso al vecchio oggetto.

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

In questi 2 casi è necessario __block:

1.Se si desidera modificare la variabile all'interno del blocco e aspettarsi che sia visibile all'esterno:

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

2.Se si desidera modificare la variabile dopo aver dichiarato il blocco e si prevede che il blocco visualizzi la modifica:

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

8

__block è un qualificatore di archiviazione che può essere utilizzato in due modi:

  1. Contrassegna che una variabile vive in un archivio condiviso tra l'ambito lessicale della variabile originale e tutti i blocchi dichiarati all'interno di tale ambito. E clang genererà una struttura per rappresentare questa variabile e utilizzerà questa struttura per riferimento (non per valore).

  2. In MRC, __block può essere utilizzato per evitare di conservare le variabili oggetto catturate da un blocco. Attenzione che questo non funziona per ARC. In ARC, invece , dovresti usare __weak .

È possibile fare riferimento al documento Apple per informazioni dettagliate.


6

__blockè un tipo di archiviazione che viene utilizzato per rendere mutevoli le variabili dell'ambito, più francamente se si dichiara una variabile con questo specificatore, il suo riferimento verrà passato ai blocchi non una copia di sola lettura per maggiori dettagli vedi Programmazione dei blocchi in iOS


2

spero che questo ti possa aiutare

supponiamo di avere un codice come:

{
     int stackVariable = 1;

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

genererà un errore del tipo "variabile non assegnabile" perché le variabili di stack all'interno del blocco sono immutabili per impostazione predefinita.

l'aggiunta di __block (modificatore di memoria) prima della sua dichiarazione lo rende mutabile all'interno del blocco, ad es __block int stackVariable=1;


1

Dalle specifiche della lingua di blocco :

Oltre al nuovo tipo di blocco, introduciamo anche un nuovo qualificatore di archiviazione, __block, per le variabili locali. [testme: una dichiarazione __block all'interno di un blocco letterale] Il qualificatore di archiviazione __block si esclude reciprocamente con i qualificatori di archiviazione locale esistenti auto, register e static. [testme] Le variabili qualificate da __block agiscono come se fossero nella memoria allocata e questa memoria è recuperato automaticamente dopo l'ultimo utilizzo di detta variabile. Un'implementazione può scegliere un'ottimizzazione in cui l'archiviazione è inizialmente automatica e solo "spostata" nella memoria allocata (heap) su una Block_copy di un blocco di riferimento. Tali variabili possono essere mutate come lo sono le normali variabili.

Nel caso in cui una variabile __block sia un blocco, si deve presumere che la variabile __block risieda nella memoria allocata e, pertanto, si presume che faccia riferimento a un blocco che si trova anche nella memoria allocata (che è il risultato di un'operazione Block_copy). Ciononostante, non è prevista l'esecuzione di Block_copy o Block_release se un'implementazione fornisce l'archiviazione automatica iniziale per Blocks. Ciò è dovuto alla condizione di competizione intrinseca di potenzialmente diversi thread che provano ad aggiornare la variabile condivisa e alla necessità di sincronizzazione per lo smaltimento di valori più vecchi e la copia di quelli nuovi. Tale sincronizzazione va oltre lo scopo di questa specifica del linguaggio.

Per i dettagli su cosa deve compilare una variabile __block, vedere la specifica di implementazione del blocco , sezione 2.3.


I tuoi collegamenti sono entrambi morti
Ben Leggiero,

Questa non è davvero una risposta e potrebbe essere arricchita o rimossa. Grazie!
Dan Rosenstark,

0

Significa che la variabile a cui è un prefisso è disponibile per essere utilizzata all'interno di un blocco.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.