覆盖调度命令

Overwrite dispatch command

我有一个发送错误消息的函数 (sendError)。然后在 5 秒后像这样删除它:

-(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 = @"";
    });
}

问题是,如果按钮是 运行 一次,然后在 3 秒后再次出现,第二次是 运行,消息将在 2 秒后而不是 5 秒后删除。

我该如何取消之前的调度并编写一个新调度(覆盖)。

有几种方法可以做到这一点。首先,您可以停止使用 GCD 并返回到更早的机制,-performSelector:withObject:afterDelay:。这里的优点是有一种方法 +cancelPreviousPerformRequestsWithTarget:selector:object: 可以取消 still-pending 执行请求。

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

如果您想继续使用 GCD,则需要自行跟踪取消。一种方法是保持 "generation" 计数。因此,假设存在一个名为 _errorGeneration.

NSUInteger 实例变量
-(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 = @"";
    });
}

基本上,您不会取消已分派的任务(因为您不能),但是如果它已过时,您可以让它什么也不做。