Iphone 使用GCD创建循环

Iphone 使用GCD创建循环,iphone,objective-c,objective-c-blocks,grand-central-dispatch,Iphone,Objective C,Objective C Blocks,Grand Central Dispatch,这就是我得到的: 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()

这就是我得到的:

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

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

我想这就是您要查找的代码:

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

多亏了

杰弗里·托马斯(Jeffrey Thomas)的答案很接近,但在ARC下,它泄漏了模块,没有ARC,它崩溃了

如果没有ARC,
\u块
变量不会保留它引用的内容。块是在堆栈上创建的。因此,
回调
变量指向堆栈上的一个块。当您第一次将
callback
传递到
dispatch\u之后(在块之外),
dispatch\u之后
成功地在堆上创建块的副本。但是,当调用该副本并再次将
回调
传递给
调度(after
后)时,
回调
是一个悬空指针(指向堆栈上现在已销毁的块),而
调度(after
后)将(通常)崩溃

使用ARC,块类型的
\u块
变量(如
回调
)会自动将块复制到堆中。所以你没有撞车。但是使用ARC时,
\u块
变量保留它引用的对象(或块)。这将导致一个保留循环:块参照自身。调用后,Xcode将在递归的
dispatch\u上显示一条警告:“在该块中强烈捕获“callback”可能会导致一个retain循环”

要解决这些问题,可以显式复制块(将其从堆栈移动到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
,只使用适合于内存管理的分支。

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

只要块创建不是非常昂贵——如果状态来自封装实例方法的任何对象,就不会如此——它就足够高效,而且简单得多

- (void) retriggerMethod
{
     ... do stuff here, assuming you want to do it on first invocation ...
     dispatch_after( ..., ^{
         [self retriggerMethod];
     });
}
您可以根据需要对其进行重组。如果你想防止同时的重触发,你可以很容易地添加一个BOOL实例变量


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

@robmayoff Leak?我看不出来。这里没有保留循环<代码>回调
dispatch\u after()自动保留,并在块完成时自动释放。所有的保留和释放都取消。我毫不怀疑,在块的实现中可能存在一些缺陷,这些缺陷会保留一个额外的引用,但我必须在分析器中看到它。请看我的答案。顺便说一句,我使用malloc历史记录测试了您的解决方案崩溃(在MRC下)和泄漏(在ARC下)。@robmayoff我能说什么?编译器和运行时是最终的评判标准。很好的解决方案。看起来你经常这样使用GCD循环。我想知道使用它比设置NSTimer有什么好处?@Gon个人偏好,真的,但有一些不同<例如,code>dispatch_*()
允许您严格控制队列。由于我懒惰,我也非常喜欢Xcode的代码完成粘贴的代码片段。:)