views:

50

answers:

3

For reference, I've already read:

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.

+1  A: 

Frequently in this type of situation, the actual error is not at the location shown in the debugger when the app halts.

For example, the debugger may be pointing to a method call, but the problem actually occurs inside the method -- not when the method is called.

To track things down, I would suggest setting a breakpoint just before the method call that triggers the error -- in your case, this would be the mateFather: withMother: call. Then step into that method. There is a good chance you will find that the problem happens inside that method -- or even inside a method called from within that method.

William Jockusch
Bleh. The debugger shows it barfing in some of Apple's code. The stack shows objc_msgSend, then ??, then _CFAutoreleasePoolPop, then [NSAutoreleasePool release]. So I'm guessing this is a case of something being double-released somehow. Must dig.
Don Jones
Accepting this because it gave me the clue that got me there - the actual answer was (as I've posted) that I was releasing an object which was also autoreleasing itself - so stepping in the debugger took me deep into some lovely Foundation binary, and the EXC_BAD_ACCESS was tossed by an autorelease pool trying to drain itself.
Don Jones
A: 

Check you have the correct property declarations of the string properties of class ThingzThing.

e.g.

@property (nonatomic, retain) NSString* breed;

NEED to be retain or copy NOT assign...

Nick H247
Seems to be. The only assigns are two C types, BOOL and int. All the NSStrings are nonatomic, retain.
Don Jones
A: 

Found it. Buried eight method calls down, I was sending release to an NSArray that was obviously autoreleasing. When the initial calling routine (viewDidAppear) fell down into autorelease mode, it tried to autorelease the already-released object, and exploded. Good grief - is there any way XCode could have helped me track that down?

In any event, in case anyone runs across this, make bloody sure you're not sending a release message to something that you didn't explicitly alloc yourself. If you didn't alloc it, odds are it was autoreleasing itself, and you sending a release to it takes the retain count negative, and the Foundation framework vomits with an EXC_BAD_ACCESS.

Don Jones