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

Abd*_*tou 15 iphone concurrency objective-c grand-central-dispatch ios

场景:

  • 用户点击一个按钮,要求对地址簿进行某种修改.
  • 调用方法以启动此修改,并显示警报视图.
  • 为了显示警报视图并保持UI响应,我使用了dispatch_queue:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                     dispatch_sync(dispatch_get_main_queue(), ^{
                       // Show the alert view
                     });
                   });
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用以下命令启动地址簿修改过程:

    dispatch_async(modifyingAddressBookQueue, ^{});
    
    Run Code Online (Sandbox Code Playgroud)

现在,我想让用户随时取消该过程(当然在保存地址簿之前).因此,当他点击警报表中的取消按钮时,我想访问调度块,设置一些特定的BOOL来停止进程并还原地址簿.

问题是,你做不到!您无法访问该块并更改其中的任何变量,因为所有变量只复制一次.块执行时块内变量的任何变化都不会被块看到.

总结一下:如何使用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;

}
Run Code Online (Sandbox Code Playgroud)

alertview代码(我自己的lib)委托

- (void) alertViewProgressCancel:(ASAlertViewProgress *)alertView { // This is a private lib.


    if (cancelledPtr)
    {
        NSLog(@"stopping");

        *cancelledPtr = YES;
    }

}
Run Code Online (Sandbox Code Playgroud)

在界面中,我宣布

BOOL*   cancelledPtr;
Run Code Online (Sandbox Code Playgroud)

更新2:

真的很令人沮丧!对于以下代码

for (NSMutableDictionary *contactDictionary in _contactArray) {

            NSLog(@"%d", _cancelModification);
            if (_cancelModification) {
                break;
            }
}
Run Code Online (Sandbox Code Playgroud)

如果_cancelModification设置为YES,则for循环被破坏,那就没问题.一旦我注释掉NSLog行,当_CancelModification变为YES时,忽略_cancelModification!

Kur*_*vis 20

如果声明BOOL使用__block,则可以在块执行之外进行更改,然后块将看到新值.有关详细信息,请参阅文档.

一个例子:

@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
Run Code Online (Sandbox Code Playgroud)

或者,在班级中使用ivar来存储BOOL.该块将隐式地复制self并通过它访问ivar.没必要__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
Run Code Online (Sandbox Code Playgroud)


vil*_*ovi 7

方法1

创建一个返回"可取消"块的自定义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");
}
Run Code Online (Sandbox Code Playgroud)

方法2

如果条件已验证,则定义用于异步执行块的宏:

#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");
    });
}
Run Code Online (Sandbox Code Playgroud)