Objective c 我是否仍要复制/Block\u复制ARC下的块?

Objective c 我是否仍要复制/Block\u复制ARC下的块?,objective-c,automatic-ref-counting,objective-c-blocks,Objective C,Automatic Ref Counting,Objective C Blocks,我刚刚偶然发现了以下主题:它有以下句子: 但是,从iOS 6开始,它们被视为常规对象,因此您无需担心 我真的被这个断言弄糊涂了,这就是为什么我要问:这个断言真的意味着Objective-C开发人员不需要这样做吗 @property(copy)blockProperties或 [^(…){…{}复制] 是否要将块及其内容从堆栈复制到堆 我希望我的描述是清楚的 请说得详细些 类似问题 我原来的答案是错的。 编辑的答案不是答案,而是“这确实是一个好问题”的东西 请参阅Matt的答案,以获取真正的参考

我刚刚偶然发现了以下主题:它有以下句子:

但是,从iOS 6开始,它们被视为常规对象,因此您无需担心

我真的被这个断言弄糊涂了,这就是为什么我要问:这个断言真的意味着Objective-C开发人员不需要这样做吗

@property(copy)blockProperties

[^(…){…{}复制]

是否要将块及其内容从堆栈复制到堆

我希望我的描述是清楚的

请说得详细些


类似问题


我原来的答案是错的。 编辑的答案不是答案,而是“这确实是一个好问题”的东西

请参阅Matt的答案,以获取真正的参考资料,了解为什么事情就是这样。

在iOS7上测试以下代码后:

int stackVar;
void (^someBlock)() = ^(){};
NSLog(@"int: %x\nblock: %x,\ncopied: %x", (unsigned int)&stackVar, (unsigned int)someBlock, (unsigned int)[someBlock copy]);
我明白了:

int: bfffda70
block: 9d9948
copied: 9d9948
这几乎意味着创建并分配到堆栈变量中的块实际上已经在堆上,复制它们不会影响任何事情

不过,这并没有得到任何官方来源的支持,因为它们仍然声明在堆栈上创建的块需要“在传递时”复制


测试前的部分答案,说明哪些文档与示例相矛盾

国家:

当您以ARC模式向堆栈上传递块时,块“仅起作用”,例如在返回中。您不再需要调用
Block\u copy
。当将堆栈“向下”传递到
数组中时,仍然需要使用
[^{}copy]
和其他执行保留操作的方法

说:

您应该指定
copy
作为属性属性,因为需要复制块以跟踪其原始范围之外的捕获状态

编辑:

事实证明,检查“捕获”变量的地址是很难解释的,并且并不总是适合确定块是否已复制到堆中或仍然驻留在堆中。尽管此处给出的块规范将充分描述事实,但我尝试了一种完全不同的方法:

街区到底是什么? 这是官方规范的摘要:

一个代码块(像一个函数)和一个包含多个数据、标志和函数指针的结构,以及一个用于“捕获变量”的可变长度部分

请注意,此结构是私有的,并由实现定义

块可以在函数范围内定义,其中该结构在堆栈本地内存中创建,也可以在全局或静态范围内定义,其中该结构在静态存储中创建

块可以“导入”其他块引用、其他变量和修改的变量

当块引用其他变量时,将导入这些变量:

  • 堆栈本地(自动)变量将通过制作“常量副本”的方式“导入”

  • 一个
    \u块
    修改后的变量将通过为指针分配另一个结构中包含的该变量的地址来导入

  • 全局变量将被简单引用(而不是“导入”)

如果要导入变量,“捕获的变量”位于可变长度部分的上述结构中。也就是说,自动变量的“对应项”(位于块外部)在块的结构中有一个存储器

由于这个“捕获的变量”是只读的,编译器可以应用一些优化:例如,如果需要块的副本,我们实际上只需要堆上捕获的变量的一个实例

当计算块文字表达式时,将设置捕获变量的值。这也意味着,捕获变量的存储必须是可写的(即,没有代码段)

捕获变量的生命周期是函数的生命周期:该块的每次调用都需要这些变量的新副本

当程序离开块的复合语句时,位于堆栈上的块中捕获的变量将被销毁

当块将被销毁时,位于堆上的块中捕获的变量将被销毁

根据Block API,版本3.5:

最初,创建块文字时,堆栈上将存在此结构:

当计算块文字表达式时,基于堆栈的结构初始化如下:

Hello block 1
Address of auto y0: 0x7fff5fbff8dc
Address of captured x0: 0x7fff5fbff940
  • 静态描述符结构的声明和初始化如下:

    Hello block 1
    Address of auto y0: 0x7fff5fbff8dc
    Address of captured x0: 0x7fff5fbff940
    
    a、 调用函数指针被设置为一个函数,该函数将块结构作为其第一个参数,其余参数(如果有)作为块,并执行块复合语句

    b、 大小字段设置为以下块文字结构的大小

    c、 如果块文字需要,则将copy\u helper和dispose\u helper函数指针设置为各自的helper函数

  • 堆栈(或全局)块文字数据结构的创建和初始化如下所示:

    Hello block 1
    Address of auto y0: 0x7fff5fbff8dc
    Address of captured x0: 0x7fff5fbff940
    
    a、 isa字段设置为外部nscuoncretestackblock的地址,该块是libSystem中提供的未初始化内存块,如果是静态或文件级块文本,则设置为nscuoncreteglobalblock


    b、 flags字段设置为零,除非有变量导入到块中,该块需要程序级Block_copy()和Block_release()操作的辅助函数,在这种情况下,(1弧将自动复制块。根据clang的文档:

    除了在初始化
    参数变量或读取
    __
    
    void foo(int param)
    {
        dispatch_queue_t queue = dispatch_queue_create("queue", 0);
    
        int x0 = param;
        dispatch_block_t block = ^{
            int y0 = x0;
            printf("Hello block 1\n");
            printf("Address of auto y0: %p\n", &y0);
            printf("Address of captured x0: %p\n", &x0);
        };
    
        block();
    }    
    
    dispatch_block_t block = ^{
        int y0 = capture_me;
    };
    dispatch_block_t otherBlock = [block copy];
    
    @property (copy) block_t completion
    
    [^{...} copy]
    
    @property dispatch_block_t completion;
    
    foo.completion = ^{ x = capture_me; ... };
    
    dispatch_async(queue, ^{int x = capture_me;});
    
    dispatch_block_t block;
    if (condition) {
        block = ^{ ... };
    }
    else {
        block = ^{ ... };
    }
    dispatch_sync(queue, block);
    
    int x0 = param;
    NSArray* array = [NSArray arrayWithObject:^{
        int y0 = x0;
        printf("Hello block 1\n");
    }];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), queue, ^{
        block_t block = array[0];
        block();
    });
    
    ^{...}
    
    [^{...} copy];  
    
    id objc_retainBlock(id x) {
        return (id)_Block_copy(x);
    }