Is Block_copy recursive?

前端 未结 3 517
伪装坚强ぢ
伪装坚强ぢ 2020-12-30 07:32

I have some code that essentially boils down to this:

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


        
相关标签:
3条回答
  • 2020-12-30 07:56

    I quote the Blocks Programming Topics guide on Apple's developer documentation site:

    When you copy a block, any references to other blocks from within that block are copied if necessary—an entire tree may be copied (from the top). If you have block variables and you reference a block from within the block, that block will be copied.

    When you copy a stack-based block, you get a new block. If you copy a heap-based block, however, you simply increment the retain count of that block and get it back as the returned value of the copy function or method.

    0 讨论(0)
  • 2020-12-30 08:00

    Yes, this is safe. You don't need to make a copy. At the time that -[Foo doSomethingElseWithBlock:] makes a copy of your literal block, it will copy the inner block to the heap.

    I wrote some test code to prove to myself that this happens; see how printer (used only in block1) is copied from the stack to the heap at the time that Block_copy(block2) is called.

    #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;
    }
    

    Transcript:

    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 讨论(0)
  • 2020-12-30 08:12

    The inner Block_copy() isn't really relevant here. What you want to keep track of is whether a given block lives on the stack or on the heap. Consider this code based on your example:

    @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;
    }
    

    This should be ok, but block and myblock are different kinds of blocks. block is a stack block and has the scope of the calling stack. It will exist until main() exits. myblock is a malloc (heap) block, and will exist until it is released. You need to make sure that you don't try to take a non-copied reference to block and use it after the stack is done. You can't stick block in an ivar without copying it.

    Joachim Bengtsson has the best write-up of this that I know of. @bbum has also written about it. (If bbum wanders in here and says I'm an idiot about this, then listen to him, but I think I'm right here.)

    0 讨论(0)
提交回复
热议问题