4

所以這裏的循環是什麼,我已經有了:創建使用GCD

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), ^{ 
       bool ready = some_function(); 
       if(ready) {      
        do_smth_here() 
       } else { 
        //invoke this block one more time after 0.1 sec 
       } 
      }); 

問題是我怎麼能得到參考當前塊?

回答

4

我通常會聲明一個實例方法,我可以調用它,在內部根據需要處理重新觸發器。這樣,任何給定的塊都是一次性的,但重新觸發創建一個新的塊。

只要塊創建不是非常昂貴 - 如果狀態來自封裝了實例方法的任何狀態,它不會非常昂貴 - 它足夠高效,而且更簡單。

- (void) retriggerMethod 
{ 
    ... do stuff here, assuming you want to do it on first invocation ... 
    dispatch_after(..., ^{ 
     [self retriggerMethod]; 
    }); 
} 

您可以根據需要對其進行重構。如果你想防止同時觸發器等,你可以很容易地添加一個BOOL實例變量...

這也提供了一個方便的取消鉤子;只需在實例中添加一個BOOL,以指示下一次調用是否應該真正執行任何操作並重新計劃。

+0

看起來你很喜歡使用GCD循環。我想知道使用它而不是設置NSTimer的優點是什麼? – Gon

+0

@Gon個人喜好,真的,但有一些差異。例如,'dispatch _ *()'可以讓你嚴格控制隊列。由於我很懶,我也非常喜歡Xcode代碼完成時粘貼的片段。 :) – bbum

1

我覺得這是你在尋找的代碼:

__block void (^callback)(); 
callback = ^{ 
    bool ready = some_function(); 
    if(ready) { 
     do_smth_here() 
    } else { 
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 
    } 
}; 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 

由於^ Blocks Tips & Tricks

+0

@robmayoff泄漏?我沒看到它。這裏沒有保留週期。 'callback'被'dispatch_after()'自動保留,並在塊完成時自動釋放。所有的保留和發佈都會被取消。我並不懷疑實現可能存在額外引用的塊的缺陷,但我必須在分析器中看到它。 –

+0

看到我的答案。順便說一句,我測試了你的解決方案使用malloc歷史記錄崩潰(在MRC下)和泄漏(在ARC下)。 –

+0

@robmayoff我能說什麼?編譯器和運行時是最終的評判者。好的解決方案 –

3

傑弗裏·托馬斯的答案是接近,但ARC下,它泄漏塊,沒有ARC,它崩潰。

沒有ARC,__block變量不保留它引用的內容。塊在堆棧上創建。所以callback變量指向堆棧上的一個塊。當您第一次(塊外)通過callbackdispatch_after時,dispatch_after成功複製了堆上的塊。但是當這個副本被調用時,並且再次通過callbackdispatch_aftercallback是一個懸掛指針(到堆棧上現在被銷燬的塊),並且dispatch_after將(通常)崩潰。

使用ARC,塊類型的__block變量(如callback自動將塊複製到堆中。所以你不會崩潰。但使用ARC時,__block變量將保留其引用的對象(或塊)。這會導致一個保留循環:塊引用它本身。 Xcode會在遞歸調用dispatch_after時向您顯示警告:「在此塊中強烈捕獲'回調'可能會導致保留週期」。

要解決這些問題,你可以明確地callback複製塊(它從堆棧移動到MRC下堆),並設置爲零(ARC下)或者釋放它(MRC下),以防止泄漏它:

__block void (^callback)() = [^{ 
     if(stop_) { 
      NSLog(@"all done"); 
#if __has_feature(objc_arc) 
      callback = nil; // break retain cycle 
#else 
      [callback release]; 
#endif 
     } else { 
      NSLog(@"still going"); 
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 
     } 
    } copy]; 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC), dispatch_get_current_queue(), callback); 

顯然你可以刪除#if,只是使用適合你的內存管理的分支。