For reference, I've already read:
- http://stackoverflow.com/questions/2981374/memory-management-question-releasing-an-object-which-has-to-be-returned
- http://stackoverflow.com/questions/3835464/iphone-return-nsmutablearray-in-method-while-still-releasing
- http://stackoverflow.com/questions/1250651/releasing-objects-returned-by-method-in-objective-c
Which I thought would help :).
This app is a teaching tool and is intended to help people visualize simple genetics. Just some background so the variable names and stuff will make sense. Here's the main code that executes when the app runs:
- (void)viewDidAppear:(BOOL)animated {
ThingzCore *core = [[ThingzCore alloc] init];
ThingzThing *thing1 = [[ThingzThing alloc] init];
thing1.breed = @"AB";
thing1.pattern = @"AC";
thing1.color = @"CA";
thing1.gender = @"XY";
thing1.generation = 1;
thing1.isEgg = NO;
ThingzThing *thing2 = [[ThingzThing alloc] init];
thing2.breed = @"CD";
thing2.pattern = @"BA";
thing2.color = @"CB";
thing2.gender = @"XX";
thing2.generation = 1;
thing2.isEgg = NO;
NSLog(@"Breeding GD BR PT CL G");
ThingzThing *child = [core mateFather:thing1 withMother:thing2];
NSLog(@"Round 1: %@ %@ %@ %@ %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
child = [core mateFather:thing1 withMother:thing2];
NSLog(@"Round 2: %@ %@ %@ %@ %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
child = [core mateFather:thing1 withMother:thing2];
NSLog(@"Round 3: %@ %@ %@ %@ %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
child = [core mateFather:thing1 withMother:thing2];
NSLog(@"Round 4: %@ %@ %@ %@ %d",child.gender,child.breed,child.pattern,child.color,child.generation);
sleep(10);
[thing1 release];
[thing2 release];
[core release];
}
And here's what happens when I run it in various ways:
- Running without breakpoints, it crashes, with no console message, after the 2nd sleep() but before the "Round 3" NSLog.
- Running with breakpoints enabled, but none defined, it runs through the entire sequence. After the fourth sleep(), it crashes with EXC_BAD_ACCESS.
- Running with breakpoints enabled and NSZombiesEnabled, it does the same thing as above - no further information, just EXC_BAD_ACCESS.
- Running in Instruments, no leaks are shown.
This is the routine being called four times:
-(ThingzThing *)mateFather:(ThingzThing *)father
withMother:(ThingzThing *)mother {
// will we be doing a mutation?
int mutationPercentage = father.generation + mother.generation;
int mutationNumber = (arc4random() % ((unsigned)100 + 1));
BOOL isMutation = NO;
if (mutationNumber <= mutationPercentage) {
isMutation = YES;
}
// get possibilities
NSArray *possibilities = [self possibilitiesByMatingFather:father
withMother:mother
mutations:isMutation];
// randomly select one of the possibilities
int keeping = (arc4random() % ((unsigned)[possibilities count]));
return [possibilities objectAtIndex:keeping];
}
Without pasting in the ENTIRE code, the possibilitiesByMatingFather:withMother:mutations function is returning an NSMutableArray. That routine declares the array by using:
NSMutableArray *possibilities = [NSMutableArray array];
It then:
return possibilities;
It does not send a release or autorelease message to possibilities; my understanding is that creating the array the way I have is an implicit autorelease. I didn't want to alloc the array, because I'm returning it, so wouldn't have the opportunity to explicitly release it.
The objects held in the possibilities NSMutableArray are of a custom class. They are added as follows:
ThingzThing *newThing = [[ThingzThing alloc] init];
newThing.breed = choiceBreed;
newThing.gender = choiceGender;
newThing.color = choiceColor;
newThing.pattern = choicePattern;
newThing.generation = mother.generation + father.generation;
newThing.name = @"";
newThing.isEgg = YES;
[possibilities addObject:newThing];
[newThing release];
Which seems to work most of the time. At least, when breakpoints are enabled, the program runs through the code without complaint until the end, as noted above.
Any suggestions on what I'm doing wrong, here? It's obviously memory management issues of some kind, but I can't sort it in my head.
BTW, in a vain, throwing-things-at-the-wall attempt to figure it out, I did modify the one line from the main routine as follows:
// get possibilities
NSArray *possibilities = [[self possibilitiesByMatingFather:father
withMother:mother
mutations:isMutation] retain];
To no avail. Same results. So the problem isn't in retaining the array returned by possibilitiesByMatingFather:withMother:mutations. Forcing a retain on that return isn't helping.