views:

56

answers:

1

I have an app where I import a bunch of data and I want to to happen on a background thread. This was working well for a small amount of data but now it seems I am running into this error midway through the parsing and importing of large amounts of my data into Core Data:

Program received signal:  “EXC_BAD_ACCESS”.

Here is the call to the background thread:

[self performSelectorInBackground:@selector(importAllData) withObject:nil];

Here is an example of what I am doing in my code:

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    // load manufactuers from DB
    NSFetchRequest *crequest    = [[NSFetchRequest alloc] init];
    NSEntityDescription *manufacturer = [NSEntityDescription entityForName:@"Manufacturer" inManagedObjectContext:managedObjectContext];
    [crequest setEntity:manufacturer];
    NSError *cerror=nil;;
    NSArray *manufacturers = [[managedObjectContext executeFetchRequest:crequest error:&cerror]mutableCopy];
    [crequest release];

for (int m=0; m < [manufacturers count]; m++) { 
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:kClientListURL, [[manufacturers objectAtIndex:m]ManufacturerID]]];

            ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
            [request setNumberOfTimesToRetryOnTimeout:2];
            [request startSynchronous];
            NSError *error = [request error];
            if (!error) {

                NSString *responseString = [request responseString];
                NSArray *items = [responseString JSONValue];

                NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
                NSNumberFormatter *dec  = [[NSNumberFormatter alloc]init];
                [dec setNumberStyle:NSNumberFormatterDecimalStyle];

                for (int i = 0; i < [items count]; i++) 
                {

                    Client *entity = (Client*) [NSEntityDescription insertNewObjectForEntityForName:@"Client" inManagedObjectContext:managedObjectContext];
                    [entity setCompanyName:[[items objectAtIndex:i] objectForKey:@"CompanyName"]];
                    // set a bunch of other properties
                    [entity setManufacturer:[manufacturers objectAtIndex:m]];

                    statusMessage = [NSString stringWithFormat:@"importing client: %@", entity.CompanyName];
                    [self performSelectorOnMainThread:@selector(setStatus) withObject:nil waitUntilDone:YES];
                }

                [f release];
                [dec release];

            } else {
                NSLog(@"%@",[NSString stringWithFormat:@"JSON parsing failed: %@", [error localizedDescription]]);
            }

            NSError *entityerror;
            if (![managedObjectContext save:&entityerror]) {
                //  //Handle the error.
                NSLog(@"\n\n\n Error saving clients: %@ \n\n\n\n",entityerror);
            }
    }
    //More data importing code

    [pool release];

As I said this was working fine for a small amount of data but now in he simulator it throws bad access errors at random points.

Then I read this article which confused me: http://www.cocoadev.com/index.pl?DebuggingAutorelease

Do I not need to release my objects if they are inside of the NSAutoReleasePool? Am I causing them to be released twice at some point?

If I remove the release statements I get potential leaks errors when building using Command-Shift-A?

A: 

The rules are:

  • If you are the owner, you need to either call [object release] or [object autorelease]. The later will delegate the release call to the NSAutoReleasePool, when the pool gets flushed or released.
  • You are an owner if you called [object retain] or if you got the object through a method that has either alloc or copy in its name.
  • If you got the object from a method that has neither alloc nor copy in its name and you haven't retained it you must not call release or autorelease on it (except in the very rare case that the documentations states that you are the owner).

So yes, if you got an autoreleased object and you call release on it you are over-releasing it. The effects are various but almost always lead to a crash, normally with EXC_BAD_ACCESS. You can debug this by setting the NSZombieEnabled environment variable. That way, whenever an object is deallocated it is replaced by a dummy that knows what object lived at that address before and can then tell you which object got over-released.

But reading through your code I didn't spot any immediately obvious problem, but I don't know Core Data (which you seem to be using) well enough. Another possibility is a threading/timing issue. Also, you said that's not the whole method ? Maybe the error is in the missing part.

DarkDust
thanks for the clarification, problem it seems was setting a NSString on the main thread to show status messages as I imported data, I'll have to adjust the way I am handling that - thanks
Slee