iPhone 应用程序中的 EXC_BAD_ACCESS - 内存管理问题

Posted

技术标签:

【中文标题】iPhone 应用程序中的 EXC_BAD_ACCESS - 内存管理问题【英文标题】:EXC_BAD_ACCESS in iPhone app - memory management issue 【发布时间】:2010-10-05 17:04:04 【问题描述】:

供参考,我已经阅读:

memory management question -- releasing an object which has to be returned iPhone: Return NSMutableArray in method while still releasing Releasing objects returned by method

我认为这会有所帮助:)。

这个应用程序是一个教学工具,旨在帮助人们想象简单的遗传学。只是一些背景,所以变量名称和东西会有意义。以下是应用运行时执行的主要代码:

- (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。 在 Instruments 中运行,未显示泄漏。

这是被调用四次的例程:

-(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];

如果不粘贴整个代码,可能性ByMatingFather: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];

无济于事。结果相同。所以问题不在于保留可能性ByMatingFather:withMother:mutations 返回的数组。强制保留该回报并没有帮助。

【问题讨论】:

我知道这是一个愚蠢的问题,但可能会导致错误。你能删除所有sleep() 行吗?嗯...只是尝试,因为我在您的代码中看不到任何错误:) 第 4 次睡眠后它会中断吗?离开此方法后是否可能引发异常?请在此方法的最后一行添加一些 NSLog-Foo 尝试将 [NSMutableArray array] 替换为 [[[NSMutableArray alloc] init] autorelease],看看会发生什么...同时发布可能性ByMatingFather 方法 移除了 sleep();不用找了。在 viewDidAppear 方法的最后添加了一个 NSLog,并得到了日志输出,但仍然得到了 EXC_BAD_ACCESS。请参阅下面对威廉建议的评论。 【参考方案1】:

在这种情况下,实际错误通常不在应用停止时调试器中显示的位置。

例如,调试器可能指向一个方法调用,但问题实际上发生在方法内部——而不是在调用方法时。

为了追踪事情,我建议在触发错误的方法调用之前设置一个断点——在你的情况下,这将是 mateFather: withMother: 调用。然后进入该方法。您很有可能会发现问题发生在该方法内部——甚至发生在从该方法调用的方法内部。

【讨论】:

嘘。调试器显示它在 Apple 的一些代码中大吵大闹。堆栈显示 objc_msgSend,然后是 ??,然后是 _CFAutoreleasePoolPop,然后是 [NSAutoreleasePool release]。所以我猜这是一个以某种方式被双重发布的情况。必须挖掘。 接受这个是因为它给了我线索让我到达那里 - 实际答案是(正如我已经发布的那样)我正在释放一个对象,它本身也是自动释放的 - 所以进入调试器需要我深入到一些可爱的 Foundation 二进制文件中,而 EXC_BAD_ACCESS 被一个试图耗尽自身的自动释放池抛出。【参考方案2】:

检查您对 ThingzThing 类的字符串属性的属性声明是否正确。

例如

@property (nonatomic, retain) NSString* breed;

需要保留或复制不分配...

【讨论】:

似乎是。唯一的赋值是两种 C 类型,BOOL 和 int。所有的 NSString 都是非原子的,保留。【参考方案3】:

找到了。埋葬了八个方法调用,我将释放发送到一个显然是自动释放的 NSArray。当初始调用例程(viewDidAppear)陷入自动释放模式时,它试图自动释放已经释放的对象,然后爆炸。很遗憾 - XCode 有什么方法可以帮助我找到它吗?

无论如何,如果有人遇到这种情况,请确保您没有向自己没有明确分配的东西发送发布消息。如果你没有分配它,它很可能是自动释放的,并且你向它发送一个释放会使保留计数为负,并且 Foundation 框架会以 EXC_BAD_ACCESS 呕吐。

【讨论】:

以上是关于iPhone 应用程序中的 EXC_BAD_ACCESS - 内存管理问题的主要内容,如果未能解决你的问题,请参考以下文章

在其他 iPhone 中安装时,iPhone 中的应用程序安装失败

iphone中的应用程序堆大小是多少?

仅 iPhone 应用程序中的 UIWebView 显示在 iPad 上

Iphone中的视频屏幕录制[重复]

将 iPhone 应用程序中的数据保存在手表应用程序中

如何使导航栏像 iPhone 中的照片应用一样透明并淡出