2013-02-27 41 views
2

时间捕捉变量值我已经在Objective-C使用GCD以下问题我想不通:为什么这个块不是在创建

我使用下面的方法来计算的东西一些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++; 
} 

问题:

我得到一个例外,因为该块试图访问self.tiles [648]! 在Apple的块文档中,我们知道块的变量值是在创建块时捕获的,所以我不明白,这甚至是可能的。我明白,加载的变量最终确实具有值648,它应该中止循环而不执行。另一些奇怪的情况是,块0中也没有使用值0,但它始于1.有时也可能看到块在加载值的循环之前,有时会丢失所有值,或者做两次。这里有一些输出:

loaded ->: 612 
loaded: 613 
loaded ->: 613 
loaded ->: 614 
loaded: 614 
loaded ->: 614 
loaded: 615 
loaded: 616 
loaded ->: 615 
loaded: 617 
loaded ->: 617 

为什么以及这怎么可能?

感谢您的任何澄清,因为我认为苹果的文档明确指出,创建该块时,应该捕获“已加载”的值,而不会再为该块进行更改。

+0

你可以显示'loaded'和'loader'的声明吗? – 2013-02-27 17:38:44

+0

它在类的私有接口中:** @ interface RasterMap(){ int loaded; } ** ____但是我也尝试过我能想到的每种变体的属性。 – 2013-02-27 17:40:10

+0

它是异步模块,看看关于GCD的WWDC 2012视频 - 完全相同!这是正常的原因,所有异步的东西从mainthread前进(并行),你可以知道什么时候。 – iiFreeman 2013-02-27 17:40:21

回答

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 

所以,该块总会看到的在运行时加载的电流值,因为它实际上是在看self->loaded

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

+0

谢谢,我已经自己找到了答案,但由于我是n00b,所以我不能很快回答自己的问题;) – 2013-02-27 19:25:56

1

所有堆栈(非静态)局部变量被封闭的词法范围捕获为const变量。 与你的情况一样,实例变量将作为正常变量访问。

要解决您的问题,请取一个具有加载值的局部变量并将其用于块处理。

int index = loaded; 

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

    index++; 
}