NSOperationQueue:在给定的超时时间后取消操作

5
基本上,如果我添加到队列中的操作在特定时间段后没有响应,我希望执行取消操作:
NSOperationQueue *   queue = ...

[self.queue addOperationWithBlock:^{
        // my block...
    } timeoutInSeconds:5.0 hasTimedOutWithBlock:^{
        // called after 5.0, operation should be canceled at the end
}];

感谢各位!

NSOperation类参考文档是一个很好的起点。 - jlehr
你在那个初始块里做什么?很少有情况是不能用另一种模式更好地解决的,但如果不知道你具体想做什么,我们很难提供建议。 - Rob
1个回答

10

你可以像你所要求的那样做,但我建议给第一个块添加一个参数,使第一个块可以检查操作是否已被取消。

[queue addOperationWithBlock:^(NSOperation *operation) {

    // do something slow and synchronous here, 

    // if this consists of a loop, check (and act upon) `[operation isCancelled]` periodically 

} timeout:5.0 timeoutBlock:^{

    // what else to do when the timeout occurred

}];

也许你不需要检查isCancelled,但在某些情况下你可能会需要(通常情况下,对取消操作的响应负担在于该操作本身),因此将其添加为谨慎参数可能是明智的。

无论如何,如果这是你想要的,你可以尝试以下操作:

@implementation NSOperationQueue (Timeout)

- (NSOperation *)addOperationWithBlock:(void (^)(NSOperation *operation))block timeout:(CGFloat)timeout timeoutBlock:(void (^)(void))timeoutBlock
{
    NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init];  // create operation
    NSBlockOperation __weak *weakOperation = blockOperation;             // prevent strong reference cycle

    // add call to caller's provided block, passing it a reference to this `operation`
    // so the caller can check to see if the operation was canceled (i.e. if it timed out)

    [blockOperation addExecutionBlock:^{
        block(weakOperation);
    }];

    // add the operation to this queue

    [self addOperation:blockOperation];

    // if unfinished after `timeout`, cancel it and call `timeoutBlock`

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // if still in existence, and unfinished, then cancel it and call `timeoutBlock`

        if (weakOperation && ![weakOperation isFinished]) {
            [weakOperation cancel];
            if (timeoutBlock) {
                timeoutBlock();
            }
        }
    });

    return blockOperation;
}

@end

提供了这段代码示例之后,我必须承认,只有在非常狭窄的情况下,像上面这样的东西才可能有用。通常最好使用另一种模式来解决这个问题。大多数情况下,当您想要一个可取消的操作时,您会实现一个 NSOperation 子类(通常是一个并发的 NSOperation 子类)。更多信息请参见 Concurrency Programming GuideOperation Queues 章节中的 Defining a Custom Operation Object 部分。


嗨,罗布,我想用它来与BLE设备通信...我想使用自定义超时处理我的每个命令...这是否属于你所考虑的非常狭窄的情况之一? - Nicolas Henin
基本上,我认为这对于网络通信非常有用...而且我认为AFNetworking具有这种模式。 - Nicolas Henin
1
不,AFNetworking是我在结尾段落中提到的模式的绝佳例子,其中您不使用上述模式,而是创建一个并发的NSOperation子类。在AFNetworking的情况下,它依赖于NSURLConnection来处理超时,并在发生超时时完成操作。此外,与上面不同,这意味着超时逻辑与网络请求的启动相关,而不是与操作的调度相关(如果它是串行队列或具有其他依赖项,则可能在将来某个时间开始)。 - Rob
@NicolasHenin 对于你的问题,关于addOperationWithBlock的超时呈现是否适用于BLE应用程序,如果它确实适用,我会感到惊讶,因为BLE请求是异步发生的,而上述假设第一个块正在同步运行。如果您使用NSOperationQueue方法,一个并发的NSOperation很可能更好地处理异步请求。但考虑到CBCentralManager委托在管理器级别(而不是请求级别),我不确定NSOperationQueue是否是BLE的正确架构。 - Rob
我正在使用LGBluetooth,这是一个在Core Bluetooth之上提供了Blocks API的封装器,但是没错,你说的很对或者使用CBCentralManager...我希望苹果公司能够尽快发布一个新的API,其中包含比当前更先进的内容...也许是Swift中的一些东西;-) - Nicolas Henin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接