2013-02-27 43 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++; 
}