Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C copy an NSArray properly?

My iPhone app keeps crashing, and I've narrowed it down to this one line over the past bloody week:

NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];

I'm getting the right result from the above, but the app crashes after accessing it (EXC_BAD_ACCESS). How do I just copy the contents of fetchResults so that I can play around with it?

I've tried

NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];

but the only thing that doesn't crash the app is

NSArray *retVal = [[NSArray alloc] initWithArray:nil];

Could someone help me out? I'm thinking I'll need a lesson in Obj-C memory management.

EDIT: Here's a more complete example of code that crashes:

NSArray *fetchResults = [managedObjectContext executeFetchRequest:request error:&error];
[request release];
NSMutableArray *retVal = [NSMutableArray arrayWithCapacity:0];
for(Job *job in fetchResults){
    //NSLog(@"dev: %@",job.lastmod_device);
    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
                            [job.jobkey copy], @"entitykey",
                            [NSNumber numberWithInt:[job.lastmod_device timeIntervalSince1970]], @"job_lastmod_device",
                            [NSNumber numberWithInt:[job.lastmod_server timeIntervalSince1970]], @"job_lastmod_server",
                            [NSNumber numberWithInt:[job.customer.lastmod_device timeIntervalSince1970]], @"customer_lastmod_device",
                            [NSNumber numberWithInt:[job.customer.lastmod_server timeIntervalSince1970]], @"customer_lastmod_server",
                            [NSNumber numberWithInt:[job.productionschedule_lastmod_device timeIntervalSince1970]], @"productionschedule_lastmod_device",
                            [NSNumber numberWithInt:[job.productionschedule_lastmod_server timeIntervalSince1970]], @"productionschedule_lastmod_server", nil];
    //NSLog(@"dict: %@", dict);
    [retVal addObject:dict];
}
return retVal;

And the code that calls this method:

NSArray *arr2 = [self retrieveJobs];

(that's it; I never even use the variable)

EDIT 2: Even just iterating over the fetched result with an empty for loop and doing nothing else with the fetchResults object makes the app crash. How is this even possible?

like image 938
kbanman Avatar asked Feb 25 '10 22:02

kbanman


4 Answers

You are thrashing; of these lines of code...

NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];

The latter two are simple leaks. The first is one way of making a copy, but retVal = [fetchResults copy]; is a better way to make a copy.

But, of course, you don't need a copy at all. That isn't the problem. You go on to say that the only thing that doesn't crash is an empty result set.

That indicates one of two things; either your result set is corrupt (unlikely) or you are accessing the result set incorrectly (likely).

The "thrashing" is that you have asked a question about a crash without including either the backtrace of the crash or the location of the crash. Add those and you'll likely get an answer in short order.

(And don't take offense at "thrashing" -- we all do it. Even after 20+ years of Objective-C, I still have to facepalm, step back, and think things through to get away from thrashing.)

like image 130
bbum Avatar answered Nov 13 '22 14:11

bbum


NSArray *retVal = [fetchResults retain] should keep everything around for you. It doesn't make a copy, but I expect that's not really what you're trying to do. Your first attempt there should make a copy. They're all prone to leaking if you're not careful though (your second example leaks guaranteed). Are you sure you're not doing something else in the program that makes this part of the code fail?

Here are some options for doing a real copy if that's what you want:

NSArray *retVal = [fetchResults copy];
NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];

Both of those return retained arrays to you.

like image 31
Carl Norum Avatar answered Nov 13 '22 14:11

Carl Norum


Are you sure the fetch request is returning data? According to the documentation:

An array of objects that meet the criteria specified by request fetched from the receiver and from the persistent stores associated with the receiver’s persistent store coordinator. If an error occurs, returns nil. If no objects match the criteria specified by request, returns an empty array.

Also, could you show the code where you access the array? Is it in the same method where you're executing the fetch request?

like image 38
kubi Avatar answered Nov 13 '22 16:11

kubi


NSArray *retVal = [[NSArray alloc] initWithArray:fetchResults];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults copy]];
NSArray *retVal = [[NSArray alloc] initWithArray:[fetchResults retain]];

Are you sure that fetchResults contains result? Because there may be a chance that "fetchResults" object itself released and pointing to some garbage location. That is why you are getting crash. Check and inform whether fetchResults is a valid object or not.

like image 1
Manjunath Avatar answered Nov 13 '22 16:11

Manjunath