将应用程序部署到设备时,程序将在几个周期后退出并出现以下错误:
Program received signal: "EXC_BAD_ACCESS".
该程序在iPhone模拟器上运行没有任何问题,只要我逐步执行一个指令,它也将调试和运行.一旦我让它再次运行,我就会EXC_BAD_ACCESS
发出信号.
在这种特殊情况下,它恰好是加速度计代码中的错误.它不会在模拟器中执行,这就是它没有抛出任何错误的原因.但是,它会在部署到设备后执行.
这个问题的大部分答案都是针对一般EXC_BAD_ACCESS
错误的,所以我会把这个问题保留为可怕的Bad Access错误.
EXC_BAD_ACCESS
通常由于非法内存访问而被抛出.您可以在下面的答案中找到更多信息.
你EXC_BAD_ACCESS
以前遇到过这个信号吗,你是怎么处理它的?
根据您的描述,我怀疑最可能的解释是您的内存管理存在一些错误.你说你已经在iPhone开发工作了几个星期,但不是你是否对Objective C有一般经验.如果你来自另一个背景,你可能需要一段时间才真正内化内存管理规则 - 除非你提出一个重点.
请记住,从分配函数(通常是静态alloc方法,但还有其他一些方法)或复制方法获得的任何内容,您也拥有内存,并且必须在完成后释放它.
但是如果你从包括工厂方法在内的任何其他方面得到回报(例如[NSString stringWithFormat]
)那么你将有一个自动释放引用,这意味着它可以在将来的某个时间通过其他代码发布 - 所以如果你需要它是至关重要的保持它超越你保留它的直接功能.如果不这样做,内存可能会在您使用它时保持分配状态,或在模拟器测试期间被释放但巧合仍然有效,但在设备上运行时更有可能被释放并显示为错误的访问错误.
跟踪这些事情的最佳方式,无论如何都是一个好主意(即使没有明显的问题)是在"工具"工具中运行应用程序,尤其是使用"泄漏"选项.
EXC_BAD_ACCESS的一个主要原因是尝试访问已发布的对象.
要了解如何解决此问题,请阅读本文档: DebuggingAutoReleasePool
即使您认为自己并未"发布自动发布的对象",这也适用于您.
这种方法非常有效.我一直使用它取得了巨大的成功!
总之,这解释了如何使用Cocoa的NSZombie调试类和命令行"malloc_history"工具来准确找到代码中已访问的已发布对象.
边注:
运行仪器和检查泄漏无助于排除EXC_BAD_ACCESS故障.我很确定内存泄漏与EXC_BAD_ACCESS无关.泄漏的定义是您无法再访问的对象,因此您无法调用它.
更新: 我现在使用Instruments来调试泄漏.从Xcode 4.2中选择Product-> Profile,当Instruments启动时,选择"Zombies".
EXC_BAD_ACCESS信号是将无效指针传递给系统调用的结果.我今天早些时候在OS X上得到了一个测试程序 - 我正在传递一个未初始化的变量pthread_join()
,这是由于早期的拼写错误.
我不熟悉iPhone开发,但你应该仔细检查你传递给系统调用的所有缓冲区指针.一直调试编译器的警告级别(使用gcc,使用-Wall
和-Wextra
选项).在模拟器/调试器上启用尽可能多的诊断.
根据我的经验,这通常是由非法内存访问引起的.检查所有指针,尤其是对象指针,以确保它们已初始化.确保您的MainWindow.xib文件(如果您使用的话)已正确设置,并具有所有必要的连接.
如果没有任何纸上检查变成任何东西,并且在单步执行时不会发生,请尝试使用NSLog()语句找到错误:将代码洒在它们上面,移动它们直到找出引起它的行错误.然后在该行上设置断点并运行程序.当你点击断点时,检查所有变量及其中的对象,看看是否有任何看起来不像你期望的那样.我会特别留意那些对象类是你没想到的变量.如果一个变量应该包含一个UIWindow但是它中有一个NSNotification,那么当调试器没有运行时,相同的底层代码错误可能以不同的方式表现出来.
我只花了几个小时跟踪一个EXC_BAD_ACCESS,发现NSZombies和其他env vars似乎没有告诉我任何事情.
对我来说,这是一个带有格式说明符但没有传递args的愚蠢的NSLog语句.
NSLog(@"Some silly log message %@-%@");
固定的
NSLog(@"Some silly log message %@-%@", someObj1, someObj2);
不是一个完整的答案,但我收到这个的一个特定情况是,当我尝试使用自动释放时,试图访问"死"的对象:
netObjectDefinedInMyHeader = [[[MyNetObject alloc] init] autorelease];
因此,例如,我实际上将此作为对象传递给'通知'(将其注册为监听器,观察者,您喜欢的任何习惯用法),但是一旦发送通知就已经死了,我将获得EXC_BAD_ACCESS.将其更改为[[MyNetObject alloc] init]
并在适当的时候释放它可以解决错误.
这可能发生的另一个原因是,例如,如果您传入一个对象并尝试存储它:
myObjectDefinedInHeader = aParameterObjectPassedIn;
稍后当尝试访问myObjectDefinedInHeader时,您可能会遇到麻烦.使用:
myObjectDefinedInHeader = [aParameterObjectPassedIn retain];
可能是你需要的.当然,这只是我遇到的几个例子,还有其他原因,但这些可能难以理解,所以我提到它们.祝好运!
Apple WWDC视频可供Apple开发者计划的任何参与者使用.这是一个很棒的视频:"会话311 - 使用乐器进行高级内存分析",其中显示了在仪器中使用僵尸和调试其他内存问题的一些示例.
有关登录页面的链接,请单击此处.
只是为了增加另一种情况:
我有代码:
NSMutableString *string; [string appendWithFormat:@"foo"];
显然我忘了为字符串分配内存:
NSMutableString *string = [[NSMutableString alloc] init]; [string appendWithFormat:@"foo"];
解决了这个问题.
我发现在objc_exception_throw上设置断点很有用.这样,当您获得EXC_BAD_ACCESS时,调试器应该会中断.
可以在此处找到有关DebuggingTechniques的说明
在它们发生之前捕获EXC_BAD_ACCESS异常的另一种方法是静态分析器,在XCode 4+中.
使用Product> Analyze(shift + cmd + B)运行静态分析器.单击分析器生成的任何消息将覆盖源上的图表,显示违规对象的保留/释放顺序.