Objective-C, cancel a dispatch queue using UI event

后端 未结 3 2036
盖世英雄少女心
盖世英雄少女心 2020-12-08 16:51

Scenario:

  • User taps a button asking for some kind of modification on address book.
  • A method is called to start this modification and an alert view is
相关标签:
3条回答
  • 2020-12-08 17:13

    If you declare your BOOL using __block, then it can be changed outside of the block execution, and the block will see the new value. See the documentation for more details.

    An example:

    @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
    

    Alternatively, use an ivar in your class to store the BOOL. The block will implicitly make a copy of self and will access the ivar via that. No need for __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
    
    0 讨论(0)
  • 2020-12-08 17:18

    Approach 1

    Create a custom dispatch_async method that returns a "cancelable" block.

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

    Approach 2

    Defining a macro for executing a block asynchronously if a condition is validated:

    #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");
        });
    }
    
    0 讨论(0)
  • 2020-12-08 17:25

    Try to apply the following code sample to your situation:

    __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]];
    });
    
    0 讨论(0)
提交回复
热议问题