Objective-C,使用UI事件取消一个调度队列

场景:

  • 用户点击一个button,要求在地址簿上进行某种修改。
  • 调用一个方法来启动这个修改,并显示一个alert视图。
  • 为了显示警报视图并保持UI的响应,我使用了dispatch_queue:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ // Show the alert view }); }); 
  • 使用以下命令启动通讯录修改过程:

     dispatch_async(modifyingAddressBookQueue, ^{}); 

现在,我想提供用户随时取消进程的能力(当然在保存地址簿之前)。 所以当他点击警报表中的取消button时,我想访问调度块,设置一些特定的BOOL来停止进程并恢复地址簿。

问题是,你不能这样做! 您不能访问该块并更改其中的任何variables,因为所有variables只被复制一次。 在执行过程中块内部的任何variables变化都不会被块看到。

总结:如何使用UI事件停止正在进行的操作?

更新:

该过程的代码:

 - (void) startFixingModification { _fixContacts = YES; __block BOOL cancelled = NO; dispatch_queue_t modifyingAddressBookQueue; modifyingAddressBookQueue = dispatch_queue_create(sModifyingAddressBookQueueIdentifier, NULL); dispatch_async(modifyingAddressBookQueue, ^{ for (NSMutableDictionary *contactDictionary in _contactArray) { if (!cancelled) { break; } i = i + 1; BOOL didFixContact = [self fixNumberInContactDictionary:contactDictionary]; if (!didFixContact) { _fixedNumbers = _fixedNumbers - 1; } dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_sync(dispatch_get_main_queue(), ^{ [self setAlertViewProgress:i]; }); }); } }); cancelledPtr = &cancelled; } 

alertview(我自己的lib)代表的代码

 - (void) alertViewProgressCancel:(ASAlertViewProgress *)alertView { // This is a private lib. if (cancelledPtr) { NSLog(@"stopping"); *cancelledPtr = YES; } } 

在界面中,我声明

 BOOL* cancelledPtr; 

更新2:

这真是令人沮丧! 为下面的代码

 for (NSMutableDictionary *contactDictionary in _contactArray) { NSLog(@"%d", _cancelModification); if (_cancelModification) { break; } } 

如果_cancelModification设置为YES,for循环被打破,那就OK了。 一旦我注释掉NSLog行,当它更改为YES时,忽略_cancel修改!

如果使用__block声明BOOL ,则可以在块执行之外进行更改,块将会看到新的值。 请参阅文档了解更多详情。

一个例子:

 @interface SNViewController () { BOOL* cancelledPtr; } @end @implementation SNViewController - (IBAction)start:(id)sender { __block BOOL cancelled = NO; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ while (!cancelled) { NSLog(@"running"); sleep(1); } NSLog(@"stopped"); }); cancelledPtr = &cancelled; } - (IBAction)stop:(id)sender { if (cancelledPtr) { NSLog(@"stopping"); *cancelledPtr = YES; } } @end 

或者,在你的课堂上使用伊娃来存储BOOL。 该块将隐含地做一个self的副本,并将通过该访问伊娃。 不需要__block

 @interface SNViewController () { BOOL cancelled; } @end @implementation SNViewController - (IBAction)start:(id)sender { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ while (!cancelled) { NSLog(@"running"); sleep(1); } NSLog(@"stopped"); }); } - (IBAction)stop:(id)sender { NSLog(@"stopping"); cancelled = YES; } @end 

方法1

创build一个返回“可取消”块的自定义dispatch_async方法。

 // The dispatch_cancel_block_t takes as parameter the "cancel" directive to suspend the block execution or not whenever the block to execute is dispatched. // The return value is a boolean indicating if the block has already been executed or not. typedef BOOL (^dispatch_cancel_block_t)(BOOL cancelBlock); dispatch_cancel_block_t dispatch_async_with_cancel_block(dispatch_queue_t queue, void (^block)()) { __block BOOL execute = YES; __block BOOL executed = NO; dispatch_cancel_block_t cancelBlock = ^BOOL (BOOL cancelled) { execute = !cancelled; return executed == NO; }; dispatch_async(queue, ^{ if (execute) block(); executed = YES; }); return cancelBlock; } - (void)testCancelableBlock { dispatch_cancel_block_t cancelBlock = dispatch_async_with_cancel_block(dispatch_get_main_queue(), ^{ NSLog(@"Block 1 executed"); }); // Canceling the block execution BOOL success1 = cancelBlock(YES); NSLog(@"Block is cancelled successfully: %@", success1?@"YES":@"NO"); // Resuming the block execution // BOOL success2 = cancelBlock(NO); // NSLog(@"Block is resumed successfully: %@", success2?@"YES":@"NO"); } 

方法2

如果条件有效,则定义macros以asynchronous执行块:

 #define dispatch_async_if(queue,condition,block) \ dispatch_async(queue, ^{\ if (condition == YES)\ block();\ }); - (void)testConditionBlock { // Creating condition variable __block BOOL condition = YES; dispatch_async_if(dispatch_get_main_queue(), condition, ^{ NSLog(@"Block 2 executed"); }); // Canceling the block execution condition = NO; // Also, we could use a method to test the condition status dispatch_async_if(dispatch_get_main_queue(), ![self mustCancelBlockExecution], ^{ NSLog(@"Block 3 executed"); }); } 

尝试将以下代码示例应用于您的情况:

 __block UIView * tempView = [[UIView alloc] initWithFrame:CGRectMake(50, 100, 220, 30)]; [tempView setBackgroundColor:[UIColor grayColor]]; [self.view addSubview:tempView]; [tempView release]; __block BOOL cancel = NO; //点击之后就会开始执行这个方法dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ int i = 0; while (i < 1000000000 && cancel == NO) { i++; } NSLog(@"Task end: i = %d", i); //这个不会执行,因为在之前,gcd task已经结束[tempView removeFromSuperview]; }); //1s 之后执行这个方法double delayInSeconds = 1.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ NSLog(@"A GCD Task Start"); cancel = YES; [tempView setBackgroundColor:[UIColor blackColor]]; });