2013-02-26 16 views
8

Ich weiß, dass eine __block Variable aus dem Stapel in den Heap verschoben wird, wenn ein Block, der darauf zugreift, kopiert wurde. Aber der folgende Testcode zeigt mir, dass die Variable __block in den Heap vor dem Kopieren des Blocks verschoben wird.Warum wird eine __block-Variable in den Heap verschoben, BEVOR der Block kopiert wird?

Das heißt, die vier Ausgänge sind: Stapel => Heap => Heap => Heap, was ist nicht mein erwartetes Ergebnis: Stapel => Stapel => Stapel => Heap.

Könnte mich jemand aufrichten?

__block int x = 0; 
int *pointerToX = &x; 
//1. It's on the stack 
NSLog(@"x's location is on the stack: %p", &x); 
int (^block)() = ^{ 
    x += 1; 
    return x; 
}; 

//2. I think its stack, but it's heap 
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //it's heap not stack 

block(); 
//3. I think its stack, but it's heap 
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //it's heap not stack 

block = [block copy]; // The variable x will be moved to the heap 
//4. I think its stack, but it's heap 
NSLog(@"x's location is on the %@: %p", (&x == pointerToX ? @"stack" : @"heap"), &x); //heap 
+0

warum Sie trotzdem über diese kümmern? und was passiert mit '__block int x; int * ptr = & x; 'wenn die Variable vom Stack zum Heap verschoben wird? –

+2

Es ist ein Implementierungsdetail. Sie sollten versuchen, nicht zu sehr darüber nachzudenken. –

+2

Das Protokollieren der Klasse des Blocks (ja, Blöcke sind Objekte) ist ebenfalls hilfreich, um zu bestimmen, wo es kopiert wurde ("__NSStackBlock__", "__NSMallocBlock__"). – CodaFi

Antwort

3

Lassen Sie mich vorweg sagen: Blöcke sind komisch.

Jetzt, wenn Sie beginnen, haben Sie eine Variable x deklariert, und auch mit __block vorangestellt. Was zum Teufel ist __block auf jeden Fall? Für Objekte, die im lexikalischen Gültigkeitsbereich des Blocks erfasst werden, werden Variablen -retain 'ed, um sicherzustellen, dass sie sich bei der Ausführung des Blocks befinden. Aber für primitive Variablen sichern Blöcke ihre Werte, indem sie gezwungen werden, sie durch den Wert const anstatt durch Referenz zu übergeben. Indem Sie __block voranstellen, haben Sie dem Compiler freie Hand gegeben, Ihre Variable "magisch" vom Stapel zum Heap zu bewegen, wenn der Block kopiert wird. Um klar zu sein, __block Variablen sind in der Tat Stapel zugeordnet, aber sie werden in den Heap (malloc() 'd) bewegt, wenn der Block kopiert wird.

Aber was ist mit den seltsamen Änderungen in der Position von x? Nun, zurück zu __block wieder. Da Sie keinen const Verweis auf x wie eine normale Variable verwenden, verwenden Blöcke einen (leicht ärgerlichen) Trick: Ein Block erzeugt einen Zeiger auf eine beliebige __block Variable, und wenn diese Variable mutiert ist, wird sie dereferenziert. Ta da! Ihre Variable wurde nicht vom Stapel auf den Heap verschoben, der Block hat lediglich den Zeiger auf den Zeiger dereferenziert und in den Speicher verschoben!

Sie sind wirklich verwirrt darüber, wo und wann Ihre Variablen bewegt werden. Ihr Beispiel protokolliert die korrekten Werte.

+0

"Aber für primitive Variablen sichern Blöcke ihre Werte, indem sie gezwungen werden, sie durch einen konstanten Wert und nicht durch einen Verweis zu übergeben." Das gilt für alle Arten. Nicht-'_block'-Variablen, unabhängig vom Typ, werden beim Erstellen des Blocks vom Wert erfasst. – newacct

2

Ihre erwartete Ausgabe basiert auf Ihrer Annahme, dass der Block erst in Schritt 3-4 kopiert wird. Dies wird jedoch in den Blocks-Spezifikationen nicht garantiert.

Ja, der Block wird spätestens kopiert, wenn Sie explizit -copy darauf aufrufen. Aber warum kann es nicht früher kopiert werden? Es ist nie falsch einen Block früher zu kopieren. Daher ist es nicht definiert, wann genau ein Block kopiert wird, und Sie sollten nicht davon abhängig sein.

Einige neuere Versionen des Compilers unter ARC können konservativ sein und einen Block sofort nach dem Erstellen kopieren. Daran ist nichts falsch. Wenn es das tut, wäre es ein Implementierungsdetail und andere Compiler oder zukünftige Versionen könnten etwas anderes machen.

0

fragte ich die gleiche Frage an In Objective-C with ARC, what does compiler do when I define a block?

Fall Sie den Code in ARC laufen

In ARC,

Blockgrößen von thermotrope Objekteigentümer Typs werden durch die Initialisierung des Stapel weg bewegt die Heap-Kopie mit dem Ergebnis des Verschiebens von der Stapelkopie.

in http://clang.llvm.org/docs/AutomaticReferenceCounting.html#blocks

+0

Wenn Sie andere Beiträge zitieren, stellen Sie bitte sicher, dass Sie die richtige Literaturangabe angeben. –

+0

JA, es wird von ARC verursacht. – jcccn