Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/27.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 当我在数组中创建一些块时,为什么索引0不应该';不可复制?;_Ios_Objective C_Copy_Automatic Ref Counting_Block - Fatal编程技术网

Ios 当我在数组中创建一些块时,为什么索引0不应该';不可复制?;

Ios 当我在数组中创建一些块时,为什么索引0不应该';不可复制?;,ios,objective-c,copy,automatic-ref-counting,block,Ios,Objective C,Copy,Automatic Ref Counting,Block,在苹果的文档中,当blk被释放时,它将崩溃,但在我运行此代码后,我看到*值:10*记录到控制台。然后应用程序崩溃。我在arr设置断点 索引0处的块是NSMallocBlock,它说它是复制到堆的,但是索引1和索引2处的块是错误的,那么这会发生什么呢?这似乎与initWithObjects签名有关: id getBlockArr() { NSInteger val = 10; return [[NSArray alloc] initWithObjects:

在苹果的文档中,当
blk
被释放时,它将崩溃,但在我运行此代码后,我看到
*值:10*
记录到控制台。然后应用程序崩溃。我在
arr
设置断点


索引0处的块是
NSMallocBlock
,它说它是复制到堆的,但是索引1和索引2处的块是错误的,那么这会发生什么呢?

这似乎与initWithObjects签名有关:

    id getBlockArr() {
        NSInteger val = 10;
        return [[NSArray alloc] initWithObjects:^{ NSLog(@"value: %ld", val);},^{ NSLog(@"value: %ld", val);},^{ NSLog(@"value: %ld", val);}, nil];
    }

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
            id arr = getBlockArr();
            void(^blk)(void) = [arr objectAtIndex:0];
            blk();
        }
        return 0;
    }
只有第一个参数是必需的,其余参数是可选的。这暗示着它可能会受到不同的对待

通常,如果您声明块变量并捕获一个变量,它们将变为
\uuuu\NSMallocBlock\uuu

- (instancetype)initWithObjects:(ObjectType)firstObj, ...;
dispatch_block_t b1 = ^{ NSLog(@"value: %ld", val); };
dispatch_block_t b2 = ^{ NSLog(@"value: %ld", val); };
dispatch_block_t b3 = ^{ NSLog(@"value: %ld", val); };
如果您不声明变量,而是在函数中传递一个块,它将成为一个
\uu NSStackBlock\uu

- (instancetype)initWithObjects:(ObjectType)firstObj, ...;
dispatch_block_t b1 = ^{ NSLog(@"value: %ld", val); };
dispatch_block_t b2 = ^{ NSLog(@"value: %ld", val); };
dispatch_block_t b3 = ^{ NSLog(@"value: %ld", val); };
区别类似于Swift中的
@转义
,而不是@escaping

堆栈销毁后,
\uuu\nstackblock\uuu
块将被销毁,当您尝试在函数返回后访问内存时,对它的访问将崩溃

请注意,新语法使所有3个块都成为
\uu NSMallocBlock\uu
,并且不会崩溃:

void f(dispatch_block_t b) {
    b();
}
void g() {
    NSInteger val = 10;
    f(^{ NSLog(@"value1: %ld", val); });
}
我敢打赌,这种怪癖是由编译器默认将所有可选C(省略号)块参数视为
\uu NSStackBlock\uu
的方式造成的


有趣的阅读:

ARC规范实际上并不保证直接传递给函数的块文本被复制。请参见ARC规范中的最后一句:

除了作为初始化的一部分完成的保留之外
\uuuuu strong
参数变量或读取
\uuuu弱
变量 这些语义要求保留块指针类型的值 具有
块拷贝
的效果优化器可能会删除此类 当它看到结果仅用作 打电话。

这样做是有意义的,因为当函数接收块类型的变量时,如果需要在调用结束后存储该块,它负责复制该变量。如果函数不需要在调用结束后存储该块(即,它只会同步使用),则无需复制。因此,在传递给函数时,不需要总是复制块(这将破坏首先将块放在堆栈上以避免过度分配的整个优化)

但是,将块传递到常规对象(非块)类型的函数参数(例如
id
NSObject*
)时会出现问题。块是Objective-C对象,因此以这种方式传递它们是完全正确的。但是,在被调用的函数中,它只知道参数是一个对象,而不知道它是一个块,因此如果它需要在调用结束后存储该对象,它将只保留它,而不是复制它。如果它是一个未被复制的堆栈块,并且在创建它的作用域结束后使用,则会发生不好的事情。为了在这种情况下更加安全,Objective-C编译器的最新版本显然会(即使ARC规范没有明确要求)始终复制块类型的值,该值会传递给非块类型的函数参数。这就是你案例中第一个论点的观点

在这种特殊情况下,第一个之后被调用函数的参数是varargs(
)。C varargs没有类型,因此编译器不知道被调用函数将决定将参数作为什么类型。可能是函数期望所有varargs参数都是特定块类型的块。或者,函数希望所有varargs参数都是常规对象(非块)类型。似乎编写总是复制传递给非块类型参数的块的编译器特性的人,并没有决定在传递给varargs时总是复制它。这就是为什么第一个之后的块没有被复制


我认为最好的做法是始终显式复制作为非块类型参数传递给函数的块,函数将在调用之外存储参数。

Ah,块的奥秘。首先,在调试器中查看崩溃,看看是否能够找出崩溃发生的原因。这是一个ARC项目,因为如果是的话,你不会自动释放NSArray。最后,将每个块分配给各个变量(
blk0
blk1
blk2
),然后将这些块组合成数组,并查看它们在
getBlockArray()
中的位置。接下来,分配一个块(
blk0
)并创建具有三个引用的数组<代码>blk0,blk0,blk0,nil这可能只是编译器优化,因为所有三个块的代码都是相同的。@JamesBucanek这是一个ARC项目,当我只在数组中插入一个块时,它运行正常。这意味着只有索引0处的块是复制的,另一个是释放的,我不知道为什么所有三个块都在数组中,所有的块都将被保留。但是,在
main()
中,第一个对象被放入自动状态,因此再次被保留,而另两个对象在
arr
超出范围时被释放。同样,在构建数组之前,将这些块放入变量中进行检查,然后查看它们在
main
中是否相同,然后在堆栈崩溃时查看堆栈(以及在哪一行崩溃),找出原因。(注意,这段代码没有什么明显的地方会使它崩溃。)这段代码非常不寻常,甚至可能是编译器错误……如果你将块声明为单独的变量,它们都会变成NSMallocBlock,并且不会崩溃。