【问题标题】:How do i resolve EXC_BAD_ACCESS errors encountered in iphone development如何解决iphone开发中遇到的EXC_BAD_ACCESS错误
【发布时间】:2009-03-03 16:55:23
【问题描述】:

我正在尝试做一件简单的事情;从互联网上读取图像,将其保存到 iphone 上应用程序的文档目录中,然后从该文件中读取它,以便我以后可以用它做其他事情。写入文件工作正常,但是当我尝试读回它时,我在 GDB 中得到一个 EXC_BAD_ACCESS 错误,我不知道如何解决。这是我的代码的基本外观:

-(UIImage *) downloadImageToFile {
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

NSString *documentsDirectory = [paths objectAtIndex:0];

[paths release]
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];

NSData * data = [[NSData alloc] initWithContentsOfURL:url];

[data writeToFile:path atomically:YES];

return [[UIImage alloc] initWithContentsOfFile:path];
}

当我尝试从文件初始化 UIImage 时,代码在 return 语句中失败。有什么想法吗?

编辑:忽略添加原来代码中存在问题的版本。

【问题讨论】:

    标签: iphone objective-c cocoa-touch file-access


    【解决方案1】:

    注意:这特别适用于非ARC内存管理。

    由于这有很多观点,并且检查过的答案适当地指出“代码显示严重缺乏关于内存管理在 Objective-C 中如何工作的知识”,但没有人指出具体错误,我想我' d 添加一个触及他们的答案。

    关于调用方法我们必须记住的基线级规则:

    • 如果方法调用包含词 allocnewcopyretain,我们拥有所创建对象的所有权。¹如果我们拥有一个对象的所有权,我们有责任释放它。

    • 如果方法调用包含这些词,我们就没有所创建对象的所有权。¹如果我们拥有一个对象,释放它不是我们的责任,因此我们不应该这样做。

    让我们看看每一行 OP 的代码:

    -(UIImage *) downloadImageToFile {
    

    我们开始了一种新方法。在这样做的过程中,我们开始了一个新的上下文,每个创建的对象都生活在这个上下文中。请记住这一点。下一行:

        NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
    

    我们拥有url:那里的词alloc告诉我们我们拥有该对象的所有权并且我们需要自己释放它。如果我们不这样做,那么代码将泄漏内存。

        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    

    我们不拥有paths:没有使用四个神奇的词,所以我们没有所有权,绝对不能自己发布。

        NSString *documentsDirectory = [paths objectAtIndex:0];
    

    我们不拥有documentsDirectory:没有魔法=没有所有权。

        [paths release]
    

    回顾几行,我们发现我们不拥有 路径,因此当我们尝试访问不再存在的内容时,此版本将导致 EXC_BAD_ACCESS 崩溃。

        NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
    

    我们不拥有path:没有魔法=没有所有权。

        NSData * data = [[NSData alloc] initWithContentsOfURL:url];
    

    我们拥有data:那里的词alloc告诉使用我们拥有对象的所有权并且我们需要自己释放它。如果我们不这样做,那么代码将泄漏内存。

    以下两行不会创建或释放任何内容。然后是最后一行:

    }
    

    方法结束,所以变量的上下文已经结束。查看代码,我们可以看到我们拥有urldata,但没有发布它们中的任何一个。因此,每次调用此方法时,我们的代码都会泄漏内存。

    NSURL 对象 url 不是很大,所以我们可能永远不会注意到泄漏,尽管它仍然应该被清理,但没有理由泄漏它。

    NSData 对象 data 是 png 图像,可能非常大;每次调用此方法时,我们都会泄漏对象的整个大小。想象一下,每次绘制表格单元格时都会调用它:不会花费很长时间来崩溃整个应用程序。

    那么我们需要做些什么来解决这些问题呢?这很简单,我们只需要在不再需要对象时立即释放它们,通常是在它们最后一次使用之后:

    -(UIImage *) downloadImageToFile {
    
        // We own this object due to the alloc
        NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
    
        // We don't own this object
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    
        // We don't own this object
        NSString *documentsDirectory = [paths objectAtIndex:0];
    
        //[paths release] -- commented out, we don't own paths so can't release it
    
        // We don't own this object
        NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
    
        // We own this object due to the alloc
        NSData * data = [[NSData alloc] initWithContentsOfURL:url];
    
        [url release]; //We're done with the url object so we can release it
    
        [data writeToFile:path atomically:YES];
    
        [data release]; //We're done with the data object so we can release it
    
        return [[UIImage alloc] initWithContentsOfFile:path];
    
        //We've released everything we owned so it's safe to leave the context
    }
    

    有些人喜欢在方法结束时上下文关闭之前立即释放所有内容。在这种情况下,[url release];[data release]; 都将出现在结束 } 大括号之前。我发现如果我尽快发布它们,代码会更清晰,当我稍后回顾它时,我会清楚地知道我在哪里完成了对象。

    总结一下:我们拥有在方法调用中使用allocnewcopyretain 创建的对象,因此必须在上下文结束之前释放它们。我们不拥有其他任何东西,并且绝不能释放它们。


    ¹这四个词实际上并没有什么神奇之处,它们只是苹果公司创建相关方法的人们一直在使用的提醒。如果我们为自己的类创建自己的初始化或复制方法,那么在其适当的方法中包含单词 alloc、new、copy 或 retain 是我们的责任,如果我们不在名称中使用它们,那么我们'需要自己记住所有权是否已过期。

    【讨论】:

    • 可能是我在这个网站上读过的最好的答案。不仅回答了问题,而且超出了解释范围。
    • 你就是男人!!!太容易了建议去阅读内存管理指南...这是一种分享知识的方式,清晰而有指导意义。谢谢。
    【解决方案2】:

    对我有很大帮助的一件事是在 objc_exception_throw 上设置一个断点。每当我即将抛出异常时,我都会点击这个断点,然后我可以调试堆栈链。我只是在我的 iPhone 项目中一直启用这个断点。

    为此,在 xcode 中转到左窗格“组和文件”底部附近并找到“断点”。打开它并单击项目断点,在详细信息窗格(顶部)中,您将看到一个标有“双击符号”的蓝色字段。双击它并输入“objc_exception_throw”。

    下次您抛出异常时,您会停下来,在调试器中,您可以返回堆栈链到导致异常的代码。

    【讨论】:

    • 谢谢,这是一个非常有用的提示。
    • 我认为这是 Xcode 现在内置的,在“运行”菜单中,“停止 Objective-C 异常”
    【解决方案3】:

    您的代码显示严重缺乏关于内存管理在 Objective-C 中如何工作的知识。除了您收到的 EXC_BAD_ACCESS 错误之外,不正确的内存管理也会导致内存泄漏,这在 iPhone 这样的小型设备上可能会导致随机崩溃。

    我建议你仔细阅读:

    Introduction to Memory Management Programming Guide for Cocoa

    【讨论】:

    • 感谢您的链接。在我的实际代码中,当我没有明确分配路径时,我试图释放路径,这后来导致了问题。你说得对,我只是从 obj-c 开始,它肯定比我想象的要复杂。
    • 没问题,希望我没有听起来太刺耳。该文档非常值得一读。祝您发展顺利!
    • 我最近收到了很多关于 EXC_BAD_ACCESS 的问题,所以我写了这个loufranco.com/blog/files/Understanding-EXC_BAD_ACCESS.html 来解释它,并附上调试技术列表。在这种情况下(太多版本),构建和分析/扫描构建可能会标记它。如果没有,启用 Zombies 肯定会有。
    【解决方案4】:

    一定要快速回顾一下内存管理规则。没有什么会导致您遇到错误,但是您正在泄漏所有分配的对象。如果您不了解保留/释放模式,则很可能在您的代码中的另一个地方您没有正确保留对象,这就是导致 EXC_BAD_ACCESS 错误的原因。

    还要注意 NSString 有处理文件系统路径的方法,你不必自己担心分隔符。

    【讨论】:

      【解决方案5】:

      不过,一般来说,如果您的代码中出现 EXC_BAD_ACCESS 并且您终生无法弄清楚原因,请尝试使用 NSZombie(不,我不是在开玩笑)。

      在 Xcode 中,展开左侧的 Executables 部分。双击与您的项目同名的列表(它应该是唯一的)。在弹出的窗口中,转到参数,然后在底部单击加号按钮。名称应为 NSZombieEnabled,值应设置为 YES

      这样,当您尝试访问已发布的对象时,您将更好地了解您正在做的事情。找出错误后,只需将值设置为 NO

      希望这对某人有所帮助!

      【讨论】:

        【解决方案6】:

        当您对内存管理不善时会出现这些错误(即对象被过早释放或类似情况)

        尝试执行以下操作..

        UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:path];
        return [myImage autorelease];
        

        我花了很多时间进行实验,同时掌握了释放/自动释放的概念。有时也需要播放 retain 关键字(尽管在这种情况下可能不需要)

        另一个选项可能只是路径不存在,或者无法读取?

        【讨论】:

        • 我知道它存在,因为我创建了文件并且它在文件系统中。我会再玩一些内存管理的东西,看看我能找到什么。
        • 从路径中去掉斜线,并确保它在项目中。是否在该目录中无关紧要,但必须将其添加到项目中才能访问它。
        • 这是不正确的——你应该自动释放返回的值,但你不应该把 is 作为类的实例变量。
        【解决方案7】:

        也许,initWithContentsOfFile 不接受路径参数?浏览 UIImage 的不同初始化方法,我认为接受路径的方法不同。

        您可能还需要做一些更有趣的事情来制作路径?我记得用“捆绑”做某事吗?抱歉说得这么含糊,我只记得这么多。

        【讨论】:

          【解决方案8】:

          从路径中去掉斜线,并确保它在项目中。它是否在该目录中并不重要,但必须将其添加到项目中才能访问它。

          【讨论】:

          • 我无法将文件添加到项目中,因为在我运行程序之前它不存在;我从其他地方读取文件后保存文件。
          • 使用 NSString 的文件系统路径操作方法,您无需担心是否应该/不应该包含斜线。不过,它与这个问题无关。
          猜你喜欢
          • 1970-01-01
          • 2016-11-21
          • 1970-01-01
          • 2011-06-08
          • 2021-07-24
          • 2019-09-10
          • 2014-10-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多