Search code examples
iosasynchronouscrashgrand-central-dispatchnsoperationqueue

GCD / NSOperationQueue EXC_BAD_ACCESS


I'm using GCD / NSOperationQueue for doing async deletes.

I have the following code implemented:

- (void)deleteWithCompletionHandler:(Handler)completionHandler
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperationWithBlock:^{

    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSError *error;

    if ([fileManager fileExistsAtPath:self.path]) {

        BOOL success = [fileManager removeItemAtPath:self.path error:&error];

    }

    NSOperationQueue *main = [NSOperationQueue mainQueue];

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{

        if (completionHandler) {

            completionHandler(error, nil);
        }
    }];
}];
}

and the following:

- (void)deleteWithCompletionHandler:(Handler)completionHandler
{
dispatch_queue_t queue = dispatch_queue_create("My Queue", NULL);

dispatch_async(queue, ^{

    NSFileManager *fileManager = [NSFileManager defaultManager];

    NSError *error;

    if ([fileManager fileExistsAtPath:self.path]) {

        BOOL success = [fileManager removeItemAtPath:self.path error:&error];

    }


    dispatch_async(dispatch_get_main_queue(), ^{

        if (completionHandler) {

            completionHandler(error, nil);
        }
    });
});
}

Both of the code snippets results in EXC_BAD_ACCESS on returning to the main thread.

What am i doing wrong? Some code in project does not uses ARC.

Thanks.


Solution

  • As you are not using ARC, your stack variables are not initialized to 0/nil. Thus completionHandler(error, nil); will be called with random stack garbage for error.

    That was the one issue that stood out when looking through your code, and the clang static analyzer should easily catch that. I suggest you run the analyzer on your code and look at all the warnings.

    There may be other issues lurking as well, so if you need more help, please provide your crash report.

    Also, your queue variable goes out of scope, so you'll either leak it (in the MRR case) or it will be released while still in use (in the ARC case). You should probably just use a global concurrent queue.