使用 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
                }
            });

问题是我如何获取对当前块的引用?

我通常不会跳过上面显示的箍,而是声明一个实例方法,我可以在内部调用该方法,根据需要处理重新触发器。 这样,任何给定的块都是一次性的,但重新触发会创建一个新块。

只要块创建不是非常昂贵 - 如果状态来自封装实例方法的任何内容,它就不会非常昂贵 - 它足够高效并且简单得多。

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

您可以根据需要对其进行重组。 如果您想防止同时重新触发等,您可以轻松添加 BOOL 实例变量......

这也为取消提供了一个方便的钩子; 只需向实例添加一个 BOOL,指示下一次调用是否真的应该执行任何操作并重新调度。

> Jeffrey Thomas的答案很接近,但在ARC下,它会泄漏块,没有ARC,它会崩溃。

如果没有 ARC,__block变量不会保留它引用的内容。 块是在堆栈上创建的。 因此,callback变量指向堆栈上的块。 当您第一次(在块外部)将callback传递给dispatch_after时,dispatch_after成功地在堆上复制了块。 但是当该副本被调用,并再次callback传递给dispatch_after时,callback是一个悬空的指针(指向堆栈上现已销毁的块),并且dispatch_after(通常)会崩溃。

使用 ARC,块类型的 __block 变量(如 callback会自动将块复制到堆中。 所以你不会崩溃。 但是对于 ARC,__block变量会保留它引用的对象(或块)。 这会导致一个保留循环:块引用自身。 Xcode 会在递归dispatch_after调用上显示警告:"在此块中强烈捕获'回调'可能会导致保留周期"。

要解决这些问题,您可以显式复制块(将其从堆栈移动到 MRC 下的堆)并将callback设置为 nil(在 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,只使用适合您的内存管理的分支。

我认为这是您要查找的代码:

__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);

感谢^块提示和技巧

最新更新