iPhone 应用程序中的 EXC_BAD_ACCESS - 内存管理问题
作为参考,我已经读过:
我认为这会有所帮助:)。
这个应用程序是一个教学工具,旨在帮助人们直观地了解简单的遗传学。只是一些背景知识,以便变量名称和内容有意义。这是应用程序运行时执行的主要代码:
- (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];
}
以下是我以各种方式运行它时发生的情况: 在
- 没有断点的情况下运行,它崩溃,没有控制台消息,在第二次 sleep() 之后但在“第 3 轮”NSLog 之前。
- 在启用断点但未定义断点的情况下运行,它会运行整个序列。第四次 sleep() 之后,它因 EXC_BAD_ACCESS 崩溃。
- 在启用断点和 NSZombiesEnabled 的情况下运行,它执行与上面相同的操作 - 没有更多信息,只有 EXC_BAD_ACCESS。
- 在仪器中运行,未显示泄漏。
这是被调用四次的例程:
-(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];
}
在不粘贴整个代码的情况下,possibilityByMatingFather:withMother:mutations 函数将返回一个 NSMutableArray。该例程通过使用以下方式声明数组:
NSMutableArray *possibilities = [NSMutableArray array];
然后:
return possibilities;
它不会向可能性发送释放或自动释放消息;我的理解是,按照我的方式创建数组是隐式自动释放。我不想分配该数组,因为我要返回它,所以没有机会显式释放它。
NSMutableArray 中保存的对象属于自定义类。它们的添加方式如下:
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];
这似乎在大多数情况下都有效。至少,当启用断点时,程序会毫无怨言地运行代码直到结束,如上所述。
关于我在这里做错了什么有什么建议吗?这显然是某种内存管理问题,但我无法在脑海中对其进行排序。
顺便说一句,在徒劳的、把东西扔到墙上的尝试中,我确实修改了主例程中的一行,如下所示:
// get possibilities
NSArray *possibilities = [[self possibilitiesByMatingFather:father
withMother:mother
mutations:isMutation] retain];
无济于事。结果相同。所以问题不在于保留由possibilityByMatingFather:withMother:mutations 返回的数组。强制保留该回报并没有帮助。
For reference, I've already read:
- memory management question -- releasing an object which has to be returned
- iPhone: Return NSMutableArray in method while still releasing
- Releasing objects returned by method
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
通常在这种情况下,当应用程序停止时,实际错误并不位于调试器中显示的位置。
例如,调试器可能指向一个方法调用,但问题实际上发生在该方法内部——而不是在调用该方法时。
为了追踪问题,我建议在触发错误的方法调用之前设置一个断点 - 在您的情况下,这将是 mateFather: withMother: 调用。然后进入该方法。您很有可能会发现问题发生在该方法内部,甚至发生在从该方法内部调用的方法内部。
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.
检查 ThingzThing 类的字符串属性的属性声明是否正确。
例如
需要保留或复制而不是分配...
Check you have the correct property declarations of the string properties of class ThingzThing.
e.g.
NEED to be retain or copy NOT assign...
找到了。埋藏了八个方法调用,我将释放发送到显然是自动释放的 NSArray。当初始调用例程(viewDidAppear)进入自动释放模式时,它尝试自动释放已经释放的对象,然后爆炸。天啊,XCode 有什么办法可以帮助我找到这个问题吗?
无论如何,如果有人遇到这个问题,一定要确保你没有向你自己没有明确分配的东西发送发布消息。如果您没有分配它,则它很可能会自行自动释放,并且您向它发送释放会导致保留计数为负,并且 Foundation 框架会抛出 EXC_BAD_ACCESS。
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.