Objective-C块

ana*_*y_v 1 block objective-c

试图了解块如何在objective-c中工作.阅读apple的文档时有下一个问题(链接)

以下是我们不应该使用块的示例:

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        blockArray[i] = ^{ printf("hello, %d\n", i); };
        // WRONG: The block literal scope is the "for" loop.
    }
}
Run Code Online (Sandbox Code Playgroud)

但是我们如何能够获得3个不同的块来打印"你好,0","你好,1"和"你好,2"?我尝试了很多不同的方法,但每次我得到"你好,2"三次.

bbu*_*bum 8

一个块开始在堆栈上生命,因此块的生命周期只与声明它的范围一样长.

for()循环的主体 - {} s中循环的主体 - 本身就是一个范围.因此,您的代码将堆栈[块]上的内容引用到周围范围[语言数组]中的变量中.

您需要将块复制到堆以使其存活:

void dontDoThis() {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        blockArray[i] = [^{ printf("hello, %d\n", i); } copy];
    }
}
Run Code Online (Sandbox Code Playgroud)

如果不使用ARC,您还需要-release在某些时候复制块.

您可能会发现这个博客帖子很方便(我在块公开后不久就写了它).这个进入了一些提示,技巧和陷阱.


等等 - 是的 - 你是对的.ARC编译器中存在着神奇的现象,导致块看起来神奇地堆在堆上.但是,我在LLVM文档中找不到明确记录此行为的任何内容.如果你关闭ARC,你会看到输出是2,2,2而不是0,1,2.

这有点新的行为.在有人能够在编译器中找到明确定义如何支持它的显式注释之前,我不会依赖此行为.


@autoreleasepool {
    void (^blockArray[3])(void);  // an array of 3 block references

    for (int i = 0; i < 3; ++i) {
        void (^block)(void) = ^{ printf("hello, %d\n", i); };
        NSLog(@"%p", block);
        blockArray[i] = block;
        NSLog(@"%p", blockArray[i]);
    }

    for (int i = 0; i < 3; ++i) blockArray[i]();
}
Run Code Online (Sandbox Code Playgroud)

输出:

2012-12-24 16:15:36.752 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.755 jkdfjkfdjkdfjk[70708:303] 0x100108160
2012-12-24 16:15:36.758 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.759 jkdfjkfdjkdfjk[70708:303] 0x100108000
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x7fff5fbff838
2012-12-24 16:15:36.760 jkdfjkfdjkdfjk[70708:303] 0x100102e70
hello, 0
hello, 1
hello, 2
Run Code Online (Sandbox Code Playgroud)

因此,在堆栈上创建块并在for()循环范围之外的赋值上自动复制到堆.

类似的测试还表明,当作为参数传递给NSArray的addObject:时,将复制该块.