Search code examples
objective-cmacoscocoaevent-dispatch-thread

Overwrite dispatch command


I have a function (sendError) that sends an error message. And then after 5 seconds removes it like this:

-(void)sendError:(NSString*)message{
    _errorMessage.stringValue = message;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        _errorMessage.stringValue = @"";
    });
}

The problem is that if the button is ran once and then again 3 seconds later the second time it is ran the message will remove 2 seconds later rather than 5.

How do I go about cancelling the previous dispatch and writing a new one (overwriting).


Solution

  • There are a couple of ways to do this. First, you can stop using GCD and go back to an earlier mechanism, -performSelector:withObject:afterDelay:. The advantage here is that there's a method +cancelPreviousPerformRequestsWithTarget:selector:object: to cancel a still-pending perform request.

    -(void)sendError:(NSString*)message{
        _errorMessage.stringValue = message;
        [NSObject cancelPreviousPerformRequestsWithTarget:_errorMessage selector:@selector(setStringValue:) object:@""];
        [_errorMessage performSelector:@selector(setStringValue:) withObject:@"" afterDelay:5];
    }
    

    If you want to keep using GCD, then you need to track the cancellation yourself. One way is to keep a "generation" count. So, assume the existence of an NSUInteger instance variable called _errorGeneration.

    -(void)sendError:(NSString*)message{
        _errorMessage.stringValue = message;
        _errorGeneration++;
        NSUInteger capturedGeneration = _errorGeneration;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (_errorGeneration == capturedGeneration)
                _errorMessage.stringValue = @"";
        });
    }
    

    Basically, you don't cancel the dispatched task (because you can't), but you make it do nothing if it's obsolete.