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,只是使用适合你的内存管理的分支。