As @HampusNilsson mentions, you can't reasonably cancel any in-flight operation in a non-garbage collected environment (such as this) because it would inherently leak resources and leave the process in an indeterminate state. NSOperationQueue
has a cancellation API, and that API can be used to implement cancellation of in-flight operations, provided that the operations themselves are cooperatively checking the flag and then cleaning up and returning early. It's not a true, hard abort.
As for canceling enqueued-but-not-started work items, yes, NSOperationQueue
handles this, but that comes at some additional expense, and NSOperationQueue
is a higher level of abstraction. GCD's performance is largely predicated on the internal use of lock-free queues. A lock-free queue is going to be faster than a lock-based implementation, but it's going to require certain trade-offs in order to achieve that speed. For instance, I would expect it to be much harder to arbitrarily mutate the queue in a lock free manner to remove a cancelled operation. I suspect that limiting the exposed queue operations to "enqueue only," and making the work items themselves immutable (blocks and function ptrs), opened the door for many of the optimizations that allow GCD to have such little overhead and perform so well.
FWIW, in the common case, making operations cancel-able is pretty trivial to implement on top of the existing GCD API, so anyone who needs this functionality can pretty easily do it themselves (and likely in a way that's better suited to their specific needs than a generalized API would be). Consider the following function -- it enqueues a block on a queue and returns a block that you could call later to cancel the enqueued operation:
dispatch_block_t dispatch_cancelable_async(dispatch_queue_t q, dispatch_block_t b)
{
__block uintptr_t isCancelled = 0;
dispatch_async(q, ^{
if (!isCancelled) b();
});
return [[^{ isCancelled = 1; } copy] autorelease];
}
This won't be the right cancellation method for every case, but it's a decent first approximation.
"Use the highest-level abstraction that gets the job done." If you want cancellation, and the difference in overhead between NSOperationQueue
and GCD is not a significant factor, you should just use NSOperationQueue
. Some would even go as far as to argue that using NSOperationQueue
is the more idiomatic choice when working in Objective-C. Beyond that, implementing non-aborting cancellation for the common case on top of GCD is, as shown, reasonably trivial.
Based on all that, my suspicion is that building in cancellation to the API was not a worthwhile trade-off in terms of performance and complexity.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…