Iphone 异步调度递归块
假设我运行以下代码:Iphone 异步调度递归块,iphone,objective-c,grand-central-dispatch,objective-c-blocks,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
__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);
从外部调用该块一次。当到达内部调用时,程序崩溃,没有任何细节。如果我在任何地方都使用直接调用而不是GCD调度,那么一切都可以正常工作
我还试着用块的副本给dispatch_打电话。我不知道这是否是朝着正确的方向迈出的一步,但这还不足以让它起作用
想法?我认为,如果你想让块继续存在,就必须复制它(当你不想让它再调用自己时释放它)。看起来除了延迟变量之外,没有什么问题。块始终使用在第1行生成的相同时间。如果您想延迟调度区块,则必须每次调用dispatch_
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
myBlock = Block_copy(^{
...
});
编辑:
我明白
块由块文本存储在堆栈中。myBlock变量替换堆栈中块的地址
从myBlock变量(堆栈中的地址)复制块后的第一次分派\u。这个地址现在是有效的。该块在当前范围内
在那之后,这个区块的范围就扩大了。myBlock变量此时的地址无效。将复制的块放入堆后分派\u。它是安全的
然后,块中的第二个dispatch_尝试从myBlock变量复制无效地址,因为堆栈中的块已被限定范围。它将在堆栈中执行损坏的块
因此,您必须阻止并复制块
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
myBlock = Block_copy(^{
...
});
别忘了积木,当你不再需要它的时候,把积木释放出来
Block_release(myBlock);
在试图解决这个问题时,我发现了一段代码,它解决了许多与递归块相关的问题。我无法再次找到源代码,但仍有代码:
// 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();
RecursiveBlock
允许使用非参数块。它可以为单个或多个参数块重写。使用这种结构简化了内存管理,例如,不可能有保留周期。我的解决方案完全源自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);
}
})();
}
您将注意到,在以下两个示例中,与递归机制交互的机制非常轻量级,基本上相当于必须在recurse
中包装一个块,并且该块必须使用BOOL*stop
变量,该变量应在某个点设置以退出递归(一些Cocoa块迭代器中的熟悉模式)
recurseAfter
的工作原理基本相同,不过这里我不会提供一个精心设计的示例。我正在使用这三种模式,取代我以前的-performBlock:afterDelay:
模式。选择自定义调度源
dispatch_queue_t queue = dispatch_queue_create( NULL, DISPATCH_QUEUE_SERIAL );
__block unsigned long steps = 0;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
dispatch_source_set_event_handler(source, ^{
if( steps == STEPS_COUNT ) {
dispatch_source_cancel(source);
return;
}
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, queue, ^{
steps += dispatch_source_get_data(source);
dispatch_source_merge_data(source, 1);
});
});
dispatch_resume( source );
dispatch_source_merge_data(source, 1);
很好。我修改了代码,但在执行递归调用时它仍然崩溃。事实上,您的代码在我的环境中运行良好。您在哪里/如何调用它?我将代码更改为使用dispatch_block__t。我设置了一个单独的项目来测试此问题。我在视图控制器的viewDidLoad方法中调用了所有代码。我现在正在使用此成功完全避免此块中的所有“捕获”可能会导致“保留周期”警告。不知道这是否对其他人有帮助,但这里有一种方法可以在递归发生时在您控制的位置执行递归块。请考虑双起始…
extern void recurseTrigger(void(^recursable)(void(^trigger)()void){RealsivBug(^(DexChuxBulkIt递归){//工作可递归(递归);})();} /代码>我相信您的来源是不可模仿的Mike Ash: