在Arc中,在什么情况下阻止__NSMallocBlock__或__NSStackBlock__或__NSGlobalBlock__?

use*_*542 2 block objective-c ios automatic-ref-counting

-(id)getBlockArray
{
    int val = 10;

    return  [[NSArray alloc] initWithObjects:^{NSLog(@"1-%d",val);},^{NSLog(@"2-%d",val);} ,^{NSLog(@"3-");}, nil];
}
Run Code Online (Sandbox Code Playgroud)

new*_*cct 6

不捕获任何变量的块是全局块。由于该块的所有实例都相同,因此编译器只能在程序生命周期内静态分配一个副本。

捕获变量(关闭)的块是堆栈或堆(malloc)块。块从堆栈开始,就像堆栈块一样。第一次复制堆栈块时,会将其移动到堆中。复制堆块不会创建另一个副本。但只是保留它。

第三个是全局块,这很明显,因为它没有捕获任何变量。前两个捕获变量。显然,第一个被复制而第二个则没有。

ARC规范不保证复制前两个块中的任何一个。但是,如果需要,ARC编译器可以插入其他副本。(复制块不会造成伤害。)

我相信,当将块指针类型的表达式传递给非块对象指针类型的参数时,Clang ARC编译器的当前版本会保守地插入副本。这是一个好主意,因为采用非块对象指针的API通常仅在需要存储它们时才保留它们,而不是像块所需的那样进行复制。事先复制它们可以防止这种情况带来的不安全。这就解释了第一个参数的复制。那第二个呢?我的猜测是,这是因为它是varargs而不是显式参数(-[NSArray initWithObjects:]采用一个参数类型id,然后具有varargs,...),而Clang关于传递时复制的规则可能不适用于varargs(因为它不知道“参数类型” ”)。

您应始终在将块传递给采用普通对象指针并存储它们的API之前复制块,例如-[NSArray initWithObjects:],因为无法保证复制。