【问题标题】:Is Block_copy recursive?Block_copy 是递归的吗?
【发布时间】:2011-06-21 05:14:26
【问题描述】:

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

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

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

【问题讨论】:

  • 我的应用程序此时不是特别可运行,所以不能。我确实找到了这个:clang.llvm.org/docs/Block-ABI-Apple.txt 其中说“块可能包含块文字表达式。内部块中使用的任何变量都被导入所有封闭的块范围,即使这些变量没有被使用。这包括 const 导入以及 __block 变量。”但是,在这种情况下,它是一个包含块的块文字,而不是那里给出的顺序。

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


【解决方案1】:

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

当您复制一个块时,如有必要,从该块内复制对其他块的任何引用 - 可能会复制整个树(从顶部)。如果你有块变量并且你从块中引用了一个块,该块将被复制。

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

【讨论】:

    【解决方案2】:

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

    我写了一些测试代码向自己证明会发生这种情况;查看在调用Block_copy(block2) 时如何将printer(仅在block1 中使用)从堆栈复制到堆中。

    #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
    

    【讨论】:

    • 只是添加一件事——即使make_block()block1 声明为__block void_block block1(例如,它可能是递归的),你仍然是安全的。 invoke() 不必担心其被调用者的存储说明符。
    【解决方案3】:

    内部的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 在这里徘徊并说我在这方面是个白痴,那就听他的,但我想我就在这里。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      • 1970-01-01
      • 2020-05-03
      • 2011-05-04
      • 1970-01-01
      相关资源
      最近更新 更多