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).
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.