Composite NSOperation. Is this a bad idea?
For an iOS4.X application I am working on, we often need to perform an HTTP request, then parse the results, and do something with the results, and so on.
For this I created an NSOperation class to allow for composition of NSOperations using an NSOperation queue. Is there any issue with using NSOperationQueues for small things like this. Some have told me that the queues should be a more permanent thing.
I don't expect the nesting to be more than 2 levels deep in our application.
Here's an example of such usage:
@implementation CompositeOperation
- (id)initWithOperations:(NSArray *)operations {
if ((self = [super init])) {
operations_ = [operations retain];
[[operations_ lastObject] addObserver:self forKeyPath:@"isFinished" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
-(void)dealloc {
[operations_ release];
[operationQueue_ release];
[super dealloc];
}
- (BOOL)isConcurrent {
return YES;
}
@synthesize isExecuting = isExecuting_;
@synthesize isFinished = isFinished_;
@synthesize operations = operations_;
- (void) start {
if (![self isCancelled]) {
operationQueue_ = [[NSOperationQueue alloc] init];
// TODO: Add code to execute this serially
[operationQueue_ addOperations:operations_ waitUntilFinished:NO];
}
}
- (void)cancel {
if (operationQueue_) {
[operationQueue_ cancelAllOperations];
}
[super cancel];
}
- (vo开发者_运维百科id)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"isFinished"] && object == [operations_ lastObject]) {
[self setIsFinished:YES];
}
}
@end
Thanks, Mike
I am the one who thinks it is a very good idea so that I even created library after it: CompositeOperations.
There are two operations: simple operation represented by COSimpleOperation
object and composite operation represented by COCompositeOperation
object.
Simple operation is a smallest possible unit - to quote documentation:
In a nutshell COSimpleOperation is a NSOperation with a small bit of convenience sugar on top of it. As an operational unit for composite operations it usually corresponds to one networking request or some small focused piece of work.
Composite operation is an operation which consists of sub-operations. To quote @mikelikespie:
The point of this object is to make it so one can represent multiple operations that are logically grouped as one operation.
...which is pretty much another description of Composite Design Pattern from Gang of Four Design Patterns.
Composite operation can be parallel
or sequential
.
Parallel operations are created just as in the code example in question:
NSArray *operations = @[
operation1, operation2, operation3
]; // each operation is NSOperation <COOperation> *
COCompositeOperation *parallelOperation = [[COCompositeOperation alloc] initWithOperations:operations];
To create sequential operation one should instantiate COCompositeOperation with an object conforming to COSequence
protocol:
Sequential composition implies sequential flow: sub-operations are executed serially one after another. Sequencing is achieved via collaboration between COCompositeOperation and arbitrary class conforming to COSequence protocol which is used by composite operation as a delegate who decides what operations are and in which order to run them.
To make this composition of operations possible I needed to put small restriction on operations library works with: aside from being NSOperations both COSimpleOperation and COCompositeOperation also conform to <COOperation>
protocol:
This conformance basically means that both operations when finished have 3 possible states:
a non-empty result field indicates success
a non-empty error field indicates failure
both empty result and error fields indicate that operation was cancelled from outside (using -[NSOperation cancel] method).
Operation can never have both result and error fields non-empty!
This convention allows Composite Operations to decide at a certain point whether to continue execution of particular group of operations or to stop it. For operations without a specific result [NSNull null] should be passed as result.
For me the rational behind this library was "just" to be able to represent operations so "that they are logically grouped as one operation". There are libraries that achieve the same kind of a higher-level functionality but at the same time they introduce concepts like: Signals in ReactiveCocoa or Promises like in PromiseKit which I don't really need or I would say do not agree with. I wanted something as simple as possible and based on good old well-known NSOperation/NSOperationQueue infrastructure so that's the point of the whole effort.
P.S. I hope this kind of answer fits the SO at least it corresponds exactly to what @mikelikespie was asking about 4 years ago.
精彩评论