Search code examples
cocoa-touchkey-value-observingnsoperation

Why is my NSOperation subclass never finishing?


I have an NSOperation subclass that I want to run concurrently.

My understanding is that for concurrent operations to work:

  • I need to define isConcurrent to return YES.
  • I need to define the start method
  • I need to send KVOs notification for isExecuting and isFinished when it's done.
  • Using @synthesize will automatically send the appropriate KVO notifications when the values for isExecuting and isFinished are changed.

Despite this, I have verified that my queue never moves on to the next item.

Here's the meat of my code:

@interface MyOperation()

@property (readwrite) BOOL isExecuting;
@property (readwrite) BOOL isFinished;

@end

@implementation MyOperation

- (void)start
{
    @autoreleasepool {
        self.isExecuting = YES;
        self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];

        _HTTPOperation.completionBlock = [^{
            [self completed];

            self.isExecuting = NO;
            self.isFinished = YES;
        } copy];

        [_HTTPOperation start];
    }
}

- (BOOL)isConcurrent
{
    return YES;
}

- (void)completed
{
}

@end

What am I missing?

(This is on an iPhone, but I can't imagine that matters.)


Solution

  • It looks like whatever KVO notifications @synthesize sends aren't enough for NSOperationQueue to move on.

    Sending the notifications manually fixes the problem:

    - (void)start
    {
        @autoreleasepool {
            [self willChangeValueForKey:@"isExecuting"];
            self.isExecuting = YES;
            [self didChangeValueForKey:@"isExecuting"];
    
            NSURLRequest *URLRequest = [self buildRequest];
            if (!URLRequest) {
                [self willChangeValueForKey:@"isFinished"];
                [self willChangeValueForKey:@"isExecuting"];
                _isExecuting = NO;
                _isFinished = YES;
                [self didChangeValueForKey:@"isExecuting"];
                [self didChangeValueForKey:@"isFinished"];
                return;
            }
    
            self.HTTPOperation = [[AFHTTPRequestOperation alloc] initWithRequest: URLRequest];
    
            _HTTPOperation.completionBlock = [^{
                [self completed];
    
                [self willChangeValueForKey:@"isFinished"];
                [self willChangeValueForKey:@"isExecuting"];
                _isExecuting = NO;
                _isFinished = YES;
                [self didChangeValueForKey:@"isExecuting"];
                [self didChangeValueForKey:@"isFinished"];
            } copy];
    
            [_HTTPOperation start];
        }
    }
    

    See also: