Combuster
Combuster

Reputation: 583

NSOperation running but missing from NSOperationQueue

I have recently been debugging a zombie issue with operations and found out that calling cancelAllOperations on the queue didn't cancel the operation in question, and in fact, the operation queue was empty even though the operation was still running.

The structure was a viewcontroller asynchronously loading a set of images off the web and perform some changes on them. Relevant (anonymised) exerpts follow:

@implementation MyViewController

- (id) init
{
    (...)
    mOperationQueue = [[NSOperationQueue alloc] init];
    (...)
}

- (void) viewDidAppear:(BOOL)animated
{
    (...)
    MyNSOperation * operation = [[MyNSOperation alloc] initWithDelegate:self andData:data];
    [mOperationQueue addOperation:operation];
    [operation release];
    (...)
}

- (void) dealloc
{
    (...)
    [mOperationQueue cancelAllOperations];
    [mOperationQueue release];
    (...)
}

- (void) imagesLoaded:(NSArray *)images
{
    (...)
}

And the operation in question:

@implementation MyNSOperation

- (id) initWithDelegate:(id)delegate andData:(NSDictionary *)data
{
    self = [super init];
    if (self)
    {
        mDelegate = delegate; // weak reference
        mData = [data retain];
        (...)
    }
    return self;
}

- (void) main
{
    NSAutoReleasePool * pool = [[NSAutoReleasePool alloc] init];

    mImages = [[NSMutableArray alloc] init];
    // load and compose images
    mAlteredImages = (...)

    [self performSelectorOnMainThread:@selector(operationCompleted) withObject:nil waitUntilDone:YES];

    [pool release];
}

- (void)operationCompleted
{
    if (![self isCancelled])
    {
        [mDelegate imagesLoaded:mAlteredImages];
    }
}

The observed flow is as follows:

The documentation of NSOperationQueue however explicitly states that "Operations remain queued until they finish their task." which looks like a breach of contract.

I've fixed the crash by keeping a reference to the operation and manually sending cancel, but I would like to know why the original approach isn't working to prevent further problems. Can someone shed some light on this?

Thanks in advance.

Upvotes: 0

Views: 526

Answers (1)

Matthias
Matthias

Reputation: 8180

cancelAllOperations does not cancel an already started operation. It only informs the operation about that fact and let the operation cancel themself, whenever it want. Thus, you can get a raise condition. Proceed with deallocacion, after you are sure that the operation is canceled.

Upvotes: 0

Related Questions