Hi there,
I have an NSOperationQueue which contains 2 NSOperations and is set to perform them one after another by setting setMaxConcurrentOperationCount
to 1.
One of the operations is a standard non-concurrent operation (just a main
method) which synchronously retrieves some data from the web (on the separate operation thread of course). The other operation is a concurrent operation as I need to use some code which has to run asynchronously.
The problem is that I have discovered that the concurrent operation only works if it is added to the queue first. If it comes after any non-concurrent operations, then strangely the start
method gets called fine, but after that method ends and I have setup my connection to callback a method, it never does. No further operations in the queue get executed after. It's as if it hangs after the start method returns, and no callbacks from any url connections get called!
If my concurrent operation is put first in the queue then it all works fine, the async callbacks work and the subsequent operation executes after it has completed. I don't understand at all!
You can see the test code for my concurrent NSOperation below, and I'm pretty sure it's solid.
Any help would be greatly appreciated!
Main Thread Observation:
I have just discovered that if the concurrent operation is first on the queue then the [start]
method gets called on the main thread. However, if it is not first on the queue (if it is after either a concurrent or non-concurrent) then the [start]
method is not called on the main thread. This seems important as it fits the pattern of my problem. What could be the reason for this?
Concurrent NSOperation Code:
@interface ConcurrentOperation : NSOperation {
BOOL executing;
BOOL finished;
}
- (void)beginOperation;
- (void)completeOperation;
@end
@implementation ConcurrentOperation
- (void)beginOperation {
@try {
// Test async request
NSURLRequest *r = [[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.google.com"]];
NSURLConnection *c = [[NSURLConnection alloc] initWithRequest:r delegate:self];
[r release];
} @catch(NSException * e) {
// Do not rethrow exceptions.
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(@"Finished loading... %@", connection);
[self completeOperation];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"Finished with error... %@", error);
[self completeOperation];
}
- (void)dealloc {
[super dealloc];
}
- (id)init {
if (self = [super init]) {
// Set Flags
executing = NO;
finished = NO;
}
return self;
}
- (void)start {
// Main thread? This seems to be an important point
NSLog(@"%@ on main thread", ([NSThread isMainThread] ? @"Is" : @"Not"));
// Check for cancellation
if ([self isCancelled]) {
[self completeOperation];
return;
}
// Executing
[self willChangeValueForKey:@"isExecuting"];
executing = YES;
[self didChangeValueForKey:@"isExecuting"];
// Begin
[self beginOperation];
}
// Complete Operation and Mark as Finished
- (void)completeOperation {
BOOL oldExecuting = executing;
BOOL oldFinished = finished;
if (oldExecuting) [self willChangeValueForKey:@"isExecuting"];
if (!oldFinished) [self willChangeValueForKey:@"isFinished"];
executing = NO;
finished = YES;
if (oldExecuting) [self didChangeValueForKey:@"isExecuting"];
if (!oldFinished) [self didChangeValueForKey:@"isFinished"];
}
// Operation State
- (BOOL)isConcurrent { return YES; }
- (BOOL)isExecuting { return executing; }
- (BOOL)isFinished { return finished; }
@end
Queuing Code
// Setup Queue
myQueue = [[NSOperationQueue alloc] init];
[myQueue setMaxConcurrentOperationCount:1];
// Non Concurrent Op
NonConcurrentOperation *op1 = [[NonConcurrentOperation alloc] init];
[myQueue addOperation:op1];
[op1 release];
// Concurrent Op
ConcurrentOperation *op2 = [[ConcurrentOperation alloc] init];
[myQueue addOperation:op2];
[op2 release];