Block_copy是递归的吗?

时间:2011-06-21 05:14:26

标签: objective-c cocoa objective-c-blocks

我有一些代码基本上归结为:

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

Foo doSomethingElseWithBlock:在其收到的块上调用Block_copyBlock_release。这在外部范围内是否也是必要的,或者内部Block_copy会处理这个吗?

3 个答案:

答案 0 :(得分:8)

我引用Apple开发者文档网站上的Blocks Programming Topics指南:

  

复制块时,如有必要,将复制对该块中其他块的任何引用 - 可以复制整个树(从顶部开始)。如果您有块变量并且您从块中引用块,则将复制该块。

     

复制基于堆栈的块时,会出现一个新块。但是,如果复制基于堆的块,则只需增加该块的保留计数,并将其作为复制函数或方法的返回值返回。

答案 1 :(得分:3)

是的,这是安全的。您无需复制。在-[Foo doSomethingElseWithBlock:]复制文字块时,它会将内部块复制到堆中。

我写了一些测试代码来向自己证明这种情况发生了;看看在printer被调用时,block1(仅在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;
}

文稿:

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

答案 2 :(得分:2)

内部Block_copy()在这里并不重要。您要跟踪的是给定块是存在于堆栈上还是堆栈上。根据您的示例考虑此代码:

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

这应该没问题,但blockmyblock是不同类型的块。 block是一个堆栈块,具有调用堆栈的范围。它会一直存在,直到main()退出。 myblock是一个malloc(堆)块,在它被释放之前一直存在。您需要确保不要尝试对block进行未复制的引用,并在完成堆栈后使用它。如果不复制它,就不能将block粘贴在ivar中。

我知道

Joachim Bengtsson有最好的写作。 @bbum也写过这篇文章。 (如果bbum在这里徘徊并且说我是个白痴,请听他说,但我想我就在这里。)