为什么这个块在创建时不会捕获变量值

Tho*_*cic 2 objective-c objective-c-blocks dispatch-async

我在使用GCD的Objective-C中遇到以下问题我无法弄清楚:

我使用以下方法计算一些648个瓷砖的东西.首先处理瓦片的顺序由设置变量"pi"的一些算法给出.变量"loaded"在此上下文中是全局变量,从0开始并正确地上升到647.

不使用块时,一切都很完美.

while (loaded < [self.tiles count]) {
    long pi = /* tricky way to calculate the position index to set */;
    NSLog(@"loaded: %d", loaded);
    // Do this in a separate thread
    dispatch_async(loader, ^{
        NSLog(@"loaded ->: %d", loaded);
        [self.tiles[loaded] setPositionIndexTo:pi];
    });

    loaded++;
}
Run Code Online (Sandbox Code Playgroud)

问题:

我得到一个例外,因为该块试图访问self.tiles [648]!在Apple的块文档中,可以看出,在创建块时,块中的变量值被捕获,所以我不明白,这是怎么回事.我确实理解加载的变量实际上最终确实具有值648,这应该在不执行的情况下中止循环.其他一些奇怪的是,值0也从未在块中使用,但它从1开始.有时也可能看到块在有关加载值的循环之前,有时会错过值,或做两次.这是一些输出:

 loaded ->: 612
 loaded: 613
 loaded ->: 613
 loaded ->: 614
 loaded: 614
 loaded ->: 614
 loaded: 615
 loaded: 616
 loaded ->: 615
 loaded: 617
 loaded ->: 617
Run Code Online (Sandbox Code Playgroud)

为什么以及如何实现这一目标?

感谢任何澄清,正如我认为Apple的文档明确指出的那样,当创建块时,应该捕获"已加载"的值而不再对块进行更改.

And*_*sen 6

问题是它loaded是一个实例变量,而不是局部变量.块捕获本地范围.在Objective-C中,当您访问实例变量时,编译器会将该访问权转换为结构成员访问权.

@interface MyClass : NSObject
{
    int varA;
}
@end

@implementation MyClass
- (void)someMethod
{
    varA = 42; // This
    self->varA = 42 // is actually this after compilation
}
@end
Run Code Online (Sandbox Code Playgroud)

因此,该块将始终在运行时看到加载的当前值,因为它实际上正在查看self->loaded.

这个问题有多种解决方案,但一般来说,您需要确保每个块都看到一个唯一的值loaded.一个简单的方法是创建loaded一个局部变量.如果需要跟踪整体进度,请从块中更新进度属性.(请注意,如果loader是并发队列,则在更新progress属性时需要注意多线程问题.一个简单的解决方案是将更新分发回自定义串行队列.)