Chris
Chris

Reputation: 489

EXC_BAD_ACCESS when adding NSOperation to NSOperationQueue

I've been sitting on this error for hours now. I'm getting an EXC_BAD_ACCESS (code=2) on the line:

[self.downloadQueue addOperation:self.downloadOP];

I know it has to be related to memory conflicts, but I just can't find the problem. The class that manages the OperationQueues is a singleton, but i think thats not the problem.

Here's the shortened version of my .h file:

@interface GTMConnectionManager : NSObject{
}

@property (retain) GTMDownloadOperation *downloadOP;
@property (retain) NSOperationQueue *downloadQueue;
// it doesn't make a difference if I add 'nonatomic' to these properties

+ (GTMConnectionManager *)sharedConnectionManager;
-(void)downloadImageData:(NSMutableArray*)p_images andController:(UIViewController*)p_resultsController;

@end

And the important part of the .m file:

#import "GTMConnectionManager.h"
@implementation GTMConnectionManager
@synthesize downloadOP, downloadQueue;

+ (GTMConnectionManager *)sharedConnectionManager
{
    static GTMConnectionManager * instance = nil;

    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        instance = [[super allocWithZone:nil] init];
    });
    return instance;
}

-(void)downloadImageData:(NSMutableArray*)p_images andController:(GTMResultsListViewController*)p_resultsController{

    self.resultsController = p_resultsController;

    [self.downloadQueue setMaxConcurrentOperationCount:2];
    self.downloadQueue = [[[NSOperationQueue alloc]init]autorelease];
    // it doesn't make a difference if I do this with or without 'autorelease'

    for (int i = 0; i < [p_images count]; i++) {
        GTMGeoImage *tmpImg = [p_images objectAtIndex:i];
        self.downloadOP = [[[GTMDownloadOperation alloc]initWithImage:tmpImg]autorelease];
        [self.downloadQueue addOperation:self.downloadOP]; //Here's the error
    }
}

When I add a breakpoint just before the error-line, both self.downloadQueue and self.downloadOP are retained correctly (not nil).

The strange this is: in this very class I have a second NSOperationQueue with other NSOperations that are declared and treated in the same way as downloadQueue and downloadOP. And they work perfectly.

And yes, GTMDownloadOperation is a child-class of NSOperation and has a -(void)main method.

I don't know what to do now. If you don't have any idea about the reason of that error, how can I analyze the situation more accurately? (Product > Analyze doesn't complain about potential leak at that position).

Thanks for your help.

Upvotes: 0

Views: 1850

Answers (4)

Siddu Halake
Siddu Halake

Reputation: 353

you're not calling [super init] inside your constructor?

Assuming you're subclassing NSOperation (or NSObject etc...), you probably should!

Upvotes: 1

Chris
Chris

Reputation: 489

Oh man... That took a while, but finally I know that the problem was in the for-loop.

The statement

self.downloadOP = [[[GTMDownloadOperation alloc]initWithImage:tmpImg]autorelease];

was accessing the variable downloadOP again and again in every iteration. Using the same NSOperation seemed to crash its retainCount.

I changed it to

GTMDownloadOperation *downloadOP = [[GTMDownloadOperation alloc]initWithImage:tmpImg];

and it works without error. Silly me.

Upvotes: 1

David Hoerl
David Hoerl

Reputation: 41662

I believe the problem is in the implementation of your operation. I suggest two courses: try creating some simple block based operations that just log hello world and add them to the queue. They will most likely wok, letting you know the queue is functional. Then start adding log messages to you subclass to see which methods get called and finish properly.

This should lead you to the problem.

Upvotes: 0

meronix
meronix

Reputation: 6176

why do you realloc and init your queue? couldn't it be just one for your singleton class?

and...

[self.downloadQueue setMaxConcurrentOperationCount:2];
self.downloadQueue = [[[NSOperationQueue alloc]init]autorelease];

the first line is executed on an old queue, then you create a new one that have no limits

when do you call the second line (alloc a new queue) you release the old one (if any)

try this:

if (!downloadQueue){
    self.downloadQueue = [[[NSOperationQueue alloc]init]autorelease];
}
[self.downloadQueue setMaxConcurrentOperationCount:2];

and remember to add:

self.downloadQueue = nil;

in your dealloc method (even if it's a singleton, and it won't be called while your app is running)

Upvotes: 0

Related Questions