异步调度的递归块

Mih*_*ian 10 iphone objective-c grand-central-dispatch objective-c-blocks

假设我运行此代码:

__block int step = 0;

__block dispatch_block_t myBlock;

myBlock = ^{
     if(step == STEPS_COUNT)
     {
         return;
     }

     step++;
     dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
     dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};

dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
Run Code Online (Sandbox Code Playgroud)

该块从外部调用一次.到达内部调用时,程序崩溃,没有任何细节.如果我在所有地方使用直接调用而不是GCD调度,一切正常.

我也尝试使用块的副本调用dispatch_after.我不知道这是否是朝着正确方向迈出的一步,但这还不足以让它发挥作用.

想法?

Ber*_*rik 16

在尝试解决此问题时,我发现了一段代码,解决了许多与递归块相关的问题.我再也找不到源了,但仍然有代码:

// in some imported file
dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) {
    return ^{ block(RecursiveBlock(block)); };
}

// in your method
dispatch_block_t completeTaskWhenSempahoreOpen = RecursiveBlock(^(dispatch_block_t recurse) {
    if ([self isSemaphoreOpen]) {
        [self completeTask];
    } else {
        double delayInSeconds = 0.3;
        dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(popTime, dispatch_get_main_queue(), recurse);
    }
});

completeTaskWhenSempahoreOpen();
Run Code Online (Sandbox Code Playgroud)

RecursiveBlock允许非参数块.可以为单个或多个参数块重写它.使用此构造简化了内存管理,例如,没有保留周期的可能性.

  • 我相信你的来源是无法模仿的Mike Ash:https://gist.github.com/mikeash/1254684 (6认同)

SG1*_*SG1 5

我的解决方案完全来自Berik的,所以他在这里得到了所有的荣誉.我只是觉得"递归块"问题空间(我还没有在其他地方找到)需要一个更通用的框架,包括异步情况,这里介绍.

使用这三个第一个定义使得第四个和第五个方法 - 这些只是示例 - 成为可能,这是一种非常简单,万无一失的,并且(我相信)以内存安全的方式将任何块递归到任意限制.

dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) {
    return ^() {
        block(RecursiveBlock(block));
    };
}

void recurse(void(^recursable)(BOOL *stop))
{
    // in your method
    __block BOOL stop = NO;
    RecursiveBlock(^(dispatch_block_t recurse) {
        if ( !stop ) {
            //Work
            recursable(&stop);

            //Repeat
            recurse();
        }
    })();
}

void recurseAfter(void(^recursable)(BOOL *stop, double *delay))
{
    // in your method
    __block BOOL stop = NO;
    __block double delay = 0;
    RecursiveBlock(^(dispatch_block_t recurse) {
        if ( !stop ) {
            //Work
            recursable(&stop, &delay);

            //Repeat
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), recurse);
        }
    })();
}
Run Code Online (Sandbox Code Playgroud)

您将注意到,在以下两个示例中,与递归机制交互的机制非常轻量级,基本上相当于必须包含一个块,recurse并且该块必须采用一个BOOL *stop变量,应该在某个点设置退出递归(一些Cocoa块迭代器中熟悉的模式).

- (void)recurseTo:(int)max
{
    __block int i = 0;
    void (^recursable)(BOOL *) = ^(BOOL *stop) {
        //Do
        NSLog(@"testing: %d", i);

        //Criteria
        i++;
        if ( i >= max ) {
            *stop = YES;
        }
    };

    recurse(recursable);
}

+ (void)makeSizeGoldenRatio:(UIView *)view
{
    __block CGFloat fibonacci_1_h = 1.f;
    __block CGFloat fibonacci_2_w = 1.f;
    recurse(^(BOOL *stop) {
        //Criteria
        if ( fibonacci_2_w > view.superview.bounds.size.width || fibonacci_1_h > view.superview.bounds.size.height ) {
            //Calculate
            CGFloat goldenRatio = fibonacci_2_w/fibonacci_1_h;

            //Frame
            CGRect newFrame = view.frame;
            newFrame.size.width = fibonacci_1_h;
            newFrame.size.height = goldenRatio*newFrame.size.width;
            view.frame = newFrame;

            //Done
            *stop = YES;

            NSLog(@"Golden Ratio %f -> %@ for view", goldenRatio, NSStringFromCGRect(view.frame));
        } else {
            //Iterate
            CGFloat old_fibonnaci_2 = fibonacci_2_w;
            fibonacci_2_w = fibonacci_2_w + fibonacci_1_h;
            fibonacci_1_h = old_fibonnaci_2;

            NSLog(@"Fibonnaci: %f %f", fibonacci_1_h, fibonacci_2_w);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

recurseAfter虽然我不会在这里提供一个人为的例子,但效果大致相同.我正在使用所有这三个没有问题,取代我的旧-performBlock:afterDelay:模式.


Wev*_*vah -1

认为如果您希望它保留下来,您必须复制该块(当您不想让它再调用自身时释放它)。