2011-06-21 14 views
12

mam jakiś kod, który zasadniczo sprowadza się do tego:Czy rekursywna jest funkcja Block_copy?

-(void)doSomethingWithBlock:(BlockTypedef)block 
{ 
    [Foo doSomethingElseWithBlock:^() { 
     block(); 
    }]; 
} 

Foo doSomethingElseWithBlock: połączeń Block_copy i Block_release na bloku, które otrzymuje. Czy jest to również konieczne w zewnętrznym zakresie, czy może to potraktować wewnętrznie?

+2

Czy spróbować? –

+0

Moja aplikacja nie jest szczególnie dostępna w tym momencie, więc nie. znalazłem to: http://clang.llvm.org/docs/Block-ABI-Apple.txt który mówi „Bloki mogą zawierać Zablokuj wyrażeń dosłownych Wszystkie zmienne stosowane w wewnętrznych bloków są importowane do całej otaczającej Block. zakresy, nawet jeśli zmienne nie są używane. Obejmuje to import const oraz zmienne __block. " Jednak w tym przypadku jest to literał blokowy zawierający blok, a nie kolejność podana tutaj. –

Odpowiedz

8

cytuję instrukcji Blocks Programming Topics na miejscu dokumentacji dla programistów Apple:

Podczas kopiowania blok, wszelkie odniesienia do innych bloków od wewnątrz tego bloku są skopiowane, jeśli to konieczne, cała drzewa mogą być kopiowane (z Top). Jeśli masz zmienne blokowe i odwołujesz się do bloku z bloku, ten blok zostanie skopiowany.

Podczas kopiowania bloku opartego na stosie otrzymuje się nowy blok. Jeśli jednak kopiujesz blok sterty, po prostu zwiększaj liczbę zatrzymań tego bloku i odzyskaj go jako zwróconą wartość funkcji lub metody kopiowania.

2

Wewnętrzny Block_copy() nie ma tu zastosowania. To, co chcesz śledzić, to to, czy dany blok żyje na stosie, czy na stercie. Rozważmy następujący kod oparty na przykład:

@interface Foo : NSObject 
@end 

@implementation Foo 

typedef void(^BlockTypedef)(void); 

+(void)doSomethingElseWithBlock:(BlockTypedef)block 
{ 
    NSLog(@"block=%@", block); 
    BlockTypedef myBlock = Block_copy(block); 
    NSLog(@"myBlock=%@", myBlock); 
    myBlock(); 
    Block_release(myBlock); 
} 

+(void)doSomethingWithBlock:(BlockTypedef)block 
{ 
    [Foo doSomethingElseWithBlock:^() { 
    block(); 
    }]; 
} 
@end 

int main (int argc, const char * argv[]) 
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 
    int i = 3; 
    BlockTypedef block = ^{ printf("i=%d\n", i); }; 
    NSLog(@"block=%@", block); 
    [Foo doSomethingWithBlock:block]; 
    block(); 
    NSLog(@"block=%@", block); 
    [pool drain]; 
    return 0; 
} 

To powinno być ok, ale block i myblock są różne rodzaje bloków. block jest blokiem stosu i ma zasięg stosu wywołującego. Będzie istnieć, dopóki nie zakończy się main(). myblock jest blokiem malloc (sterty) i będzie istnieć, dopóki nie zostanie zwolniony. Musisz upewnić się, że nie próbujesz pobrać niekopiowanego odniesienia do block i użyć go po ukończeniu stosu. Nie można trzymać block w ivar bez jego kopiowania.

Joachim Bengtsson ma najlepszy opis tego, co znam. @bbum również o tym pisał. (Jeśli bbum błądzi tutaj i mówi, że jestem idiotą, to posłuchaj go, ale myślę, że jestem tutaj.)

3

Tak, to jest bezpieczne. Nie musisz wykonywać kopii. W momencie, gdy -[Foo doSomethingElseWithBlock:] tworzy kopię twojego literalnego bloku, skopiuje on wewnętrzny blok do sterty.

Napisałem trochę kodu testowego, aby udowodnić sobie, że tak się dzieje; zobacz, jak printer (używane tylko w block1) jest kopiowane ze stosu do sterty w momencie wywołania Block_copy(block2).

#include <Block.h> 
#include <dispatch/dispatch.h> 
#include <stdio.h> 

typedef void (^void_block)(); 

class ScopedPrinter { 
    public: 
    ScopedPrinter() { 
     printf("construct %p\n", this); 
    } 
    ScopedPrinter(const ScopedPrinter& other) { 
     printf("copy %p <- %p\n", this, &other); 
    } 
    ~ScopedPrinter() { 
     printf("destroy %p\n", this); 
    } 
}; 

void_block invoke(void_block other) { 
    printf("other %p\n", (void*)other); 
    void_block block2 = ^{ 
     printf("other %p\n", (void*)other); 
     other(); 
    }; 
    printf("block2 created\n"); 
    block2 = Block_copy(block2); 
    printf("block2 copied\n"); 
    return block2; 
} 

void_block make_block() { 
    ScopedPrinter printer; 
    printf("printer created\n"); 
    void_block block1 = ^{ 
     printf("block1 %p\n", &printer); 
    }; 
    printf("block1 created\n"); 
    return invoke(block1); 
} 

int main() { 
    void_block block = make_block(); 
    block(); 
    Block_release(block); 
    return 0; 
} 

odpisu:

construct 0x7fff6a23fa70 
printer created 
copy 0x7fff6a23fa50 <- 0x7fff6a23fa70 
block1 created 
other 0x7fff6a23fa30 
block2 created 
copy 0x10a700970 <- 0x7fff6a23fa50 
block2 copied 
destroy 0x7fff6a23fa50 
destroy 0x7fff6a23fa70 
other 0x10a700950 
block1 0x10a700970 
destroy 0x10a700970 
+0

Wystarczy dodać jedną rzecz - nawet jeśli 'make_block()' zadeklarował 'blok1' jako' __block void_block block1' (np. Aby mógł być rekurencyjny), nadal byłbyś bezpieczny.'invoke()' nie musi się martwić o specyfikatory pamięci jego obiektów. – sfiera

Powiązane problemy