objective-c 如何解决iphone开发中遇到的EXC_BAD_ACCESS错误
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/607222/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do i resolve EXC_BAD_ACCESS errors encountered in iphone development
提问by Kevlar
I'm trying to do a simple thing; read an image from the internet, save it to the app's documents directory on the iphone, and read it back from that file so that i can do other things with it later. Writing the file works fine but when i try to read it back i get an EXC_BAD_ACCESS error in GDB that i have no idea how to resolve. Here is what my code basically looks like:
我正在尝试做一件简单的事情;从互联网上读取图像,将其保存到 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];
}
The code fails in the return statement when i try to initialize the UIImage from the file. Any ideas?
当我尝试从文件初始化 UIImage 时,代码在 return 语句中失败。有任何想法吗?
Edit: neglected to add a release that was the problem in the code originally.
编辑:忽略添加最初是代码中存在问题的版本。
采纳答案by August
Your code shows a severe lack of knowledge of how memory management works in Objective-C. In addition to the EXC_BAD_ACCESS errors you're receiving, improper memory management also causes memory leaks which, on a small device like the iPhone, can lead to random crashes.
您的代码严重缺乏对 Objective-C 中内存管理工作方式的了解。除了您收到的 EXC_BAD_ACCESS 错误之外,不正确的内存管理还会导致内存泄漏,这在 iPhone 等小型设备上可能会导致随机崩溃。
I recommend you give this a thorogh read:
我建议你仔细阅读:
Introduction to Memory Management Programming Guide for Cocoa
回答by Matthew Frederick
Note:This applies specifically to non-ARCmemory management.
注意:这特别适用于非 ARC内存管理。
Since this has had so many views and the checked answer appropriately states that "code shows a severe lack of knowledge of how memory management works in Objective-C," yet no one has pointed the specific errors out, I figure I'd add an answer that touched on them.
由于这有如此多的观点,并且检查的答案适当地指出“代码显示严重缺乏对 Objective-C 中内存管理工作方式的了解”,但没有人指出具体的错误,我想我会添加一个触及他们的答案。
The baseline-level rule that we have to remember about calling methods:
关于调用方法,我们必须记住的基线级别规则:
If the method call includes the words alloc, new, copy, or retain, we have ownership of the object created.¹ If we have ownership of an object, it's our responsibility to release it.
If the method call does notcontain those words, we do not have ownership of the object created.¹ If we don'thave ownership of an object, releasing it is notour responsibility, and we therefore should never do it.
如果方法调用包含alloc、new、copy或retain字样,我们就拥有所创建对象的所有权。¹ 如果我们拥有对象的所有权,则我们有责任释放它。
如果方法调用不包含这些词,我们就没有所创建对象的所有权。¹如果我们没有对象的所有权,释放它不是我们的责任,因此我们永远不应该这样做。
Let's look at each line the OP's code:
让我们看看 OP 代码的每一行:
-(UIImage *) downloadImageToFile {
We started a new method. In doing so we have started a new context in which each of the created objects lives. Keep this in mind for a bit. The next line:
我们开始了一种新方法。在这样做的过程中,我们启动了一个新的上下文,每个创建的对象都存在于其中。请记住这一点。下一行:
NSURL * url = [[NSURL alloc] initWithString: self.urlField.text];
We own url: the word allocthere tells us that we have ownership of the object and that we will need to release it ourselves. If we do not then the code will leak memory.
我们拥有url:alloc这个词告诉我们我们拥有对象的所有权,我们需要自己释放它。如果我们不这样做,那么代码将泄漏内存。
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
We do not own paths: no use of the four magic words, so we do not have ownership and must never release it ourselves.
我们不拥有paths:没有使用四个魔法词,所以我们没有所有权,绝对不能自己释放。
NSString *documentsDirectory = [paths objectAtIndex:0];
We do not own documentsDirectory: no magic words = no ownership.
我们不拥有documentsDirectory:没有魔法词 = 没有所有权。
[paths release]
Going back a couple of lines we see that we don't own paths, so this release will cause an EXC_BAD_ACCESS crash as we try to access something that no longer exists.
回顾几行,我们看到我们没有自己的路径,因此当我们尝试访问不再存在的内容时,此版本将导致 EXC_BAD_ACCESS 崩溃。
NSString * path = [documentsDirectory stringByAppendingString:@"/testimg.png"];
We do not own path: no magic words = no ownership.
我们不拥有path:没有魔法词 = 没有所有权。
NSData * data = [[NSData alloc] initWithContentsOfURL:url];
We own data: the word allocthere tells use that we have ownership of the object and that we will need to release it ourselves. If we do not then the code will leak memory.
我们拥有data:alloc这个词告诉 use 我们拥有对象的所有权,我们需要自己释放它。如果我们不这样做,那么代码将泄漏内存。
The following two lines don't create or release anything. Then comes the last line:
以下两行不会创建或释放任何内容。然后是最后一行:
}
The method is over, so the context for the variables has ended. Looking at the code we can see that we owned both urland data, but didn't release either of them. As a result our code will leak memory every time this method is called.
该方法已结束,因此变量的上下文已结束。查看代码,我们可以看到我们同时拥有url和data,但没有释放它们中的任何一个。因此,每次调用此方法时,我们的代码都会泄漏内存。
The NSURLobject urlisn't very big, so it's possible that we might never notice the leak, though it should still be cleaned up, there's no reason to leak it.
该NSURL物体url是不是很大,所以这是可能的,我们可能永远不会发现泄漏,但它仍然应该被清理,没有任何理由泄漏它。
The NSDataobject datais a png image, and could be very large; we are leaking the entire size of the object every time this method is called. Imagine this was called every time a table cell was drawn: it wouldn't take long at all to crash the whole app.
该NSData对象data是一个 png 图像,并且可能非常大;每次调用此方法时,我们都会泄漏对象的整个大小。想象一下,每次绘制表格单元格时都会调用它:整个应用程序崩溃根本不会花很长时间。
So what do we need to do to fix the problems? It's pretty simple, we simply need to release the objects as soon as we no longer need them, commonly right after the last time they're used:
那么我们需要做些什么来解决这些问题呢?这很简单,我们只需要在不再需要对象时立即释放它们,通常是在最后一次使用它们之后:
-(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
}
Some people prefer to release everything at once, right before the context closes at the end of the method. In that case both [url release];and [data release];would appear right before the closing }brace. I find that if I release them as soon as I can the code is clearer, making it clear when I go over it later exactly where I'm done with objects.
有些人喜欢在方法结束时上下文关闭之前立即释放所有内容。在这种情况下,[url release];和[data release];都会出现在右}大括号之前。我发现如果我尽快释放它们,代码会更清晰,当我稍后再查看它时,我会在我完成对象的地方明确说明。
To summarize: we own objects created with alloc, new, copy, or retainin the method calls so must release them before the context ends. We don't own anything else and must never release them.
总结一下:我们拥有使用alloc, new, copy, 或retain在方法调用中创建的对象,因此必须在上下文结束之前释放它们。我们不拥有其他任何东西,绝不能释放它们。
¹There's nothing actually magical in the four words, they're just a consistently-used reminder by the folks at Apple who created the methods in question. If we create our own initialization or copying methods for a class of our own then the inclusion of the words alloc, new, copy, or retain in its appropriate methods is our responsibility, and if we don't use them in our names then we'll need to remember for ourselves whether ownership has passed.
¹这四个词实际上并没有什么神奇之处,它们只是 Apple 创造相关方法的人们一直在使用的提醒。如果我们为我们自己的类创建我们自己的初始化或复制方法,那么在其适当的方法中包含单词 alloc、new、copy 或 retain 是我们的责任,如果我们不在我们的名字中使用它们,那么我们需要自己记住所有权是否已经过去。
回答by Jon Thomason
One thing that helps me a lot is to have a breakpoint on objc_exception_throw. Anytime I'm about to get an exception thrown, I hit this breakpoint and I can debug back up the stack chain. I just leave this breakpoint enabled all the time in my iPhone projects.
对我有很大帮助的一件事是在 objc_exception_throw 上设置断点。每当我将要抛出异常时,我都会遇到这个断点,然后我可以调试堆栈链。我只是在我的 iPhone 项目中一直启用这个断点。
To do this, in xcode go to near the bottom of the left pane "Groups & Files" and find "Breakpoints". Open it and click on Project Breakpoints and in the detail pane (top), you'll see a blue field labeled "Double-Click for Symbol." Double-click on it and enter "objc_exception_throw".
为此,在 xcode 中转到左窗格底部附近的“组和文件”并找到“断点”。打开它并单击 Project Breakpoints,在详细信息窗格(顶部)中,您将看到一个标有“双击符号”的蓝色字段。双击它并输入“objc_exception_throw”。
Next time you throw an exception, you'll stop and in the debugger, you can walk back up the stack chain to your code that caused the exception.
下次抛出异常时,您将停止并在调试器中返回堆栈链,返回导致异常的代码。
回答by Marc Charbonneau
Definitely give memory management rules a quick review. Nothing jumps out that would cause the error you're getting, but you're leaking all those objects your allocating. If you don't understand the retain/release pattern, chances are there's another spot in your code where you're not retaining an object properly, and that's whats causing the EXC_BAD_ACCESS error.
一定要快速回顾一下内存管理规则。没有什么会导致您得到错误的跳出,但是您正在泄漏您分配的所有对象。如果您不了解保留/释放模式,则您的代码中可能还有另一个地方您没有正确保留对象,这就是导致 EXC_BAD_ACCESS 错误的原因。
Also note that NSString has methods for dealing with filesystem paths, you should never have to worry about the separator yourself.
另请注意, NSString 具有处理文件系统路径的方法,您不必自己担心分隔符。
回答by FeifanZ
In general though, if you're getting EXC_BAD_ACCESSs in your code and you can't for the life of you figure out why, try using NSZombie (no, I'm not kidding).
但总的来说,如果您在代码中获得 EXC_BAD_ACCESSs 并且您终生无法弄清楚原因,请尝试使用 NSZombie(不,我不是在开玩笑)。
In Xcode, expand the Executables section on the left. Double click on the listing that has the same name as your project (it should be the only one). In the window that pops up, go to Arguments, and in the bottom portion, click the plus button. The name should be NSZombieEnabledand the value should be set to YES
在 Xcode 中,展开左侧的 Executables 部分。双击与您的项目同名的列表(它应该是唯一的)。在弹出的窗口中,转到参数,然后在底部单击加号按钮。名称应为NSZombieEnabled且值应设置为YES
This way, when you try to access a released object, you'll have a better of what you're doing. Just set the value to NOonce you've figured out the bug.
这样,当您尝试访问已发布的对象时,您将更好地了解自己在做什么。找出错误后,只需将该值设置为NO。
Hope this helps someone!
希望这可以帮助某人!
回答by adam
These errors occur when you are mismanaging memory (ie. an object is being released prematurely or similar)
当您对内存管理不善时会发生这些错误(即对象过早释放或类似)
Try doing something like the following..
尝试执行以下操作..
UIImage *myImage = [[UIImage alloc] initWithContentsOfFile:path];
return [myImage autorelease];
I spent a lot of time experimenting whilst getting to grips with the concepts of release/autorelease. Sometimes the retain keyword needs to be played also (although probably not in this case)
我花了很多时间进行实验,同时掌握了发布/自动发布的概念。有时也需要播放保留关键字(尽管在这种情况下可能不会)
Another option could be simply the path does not exist, or cannot be read from?
另一种选择可能只是路径不存在,或者无法读取?
回答by Greg
Perhaps, initWithContentsOfFile doesn't take a path argument? Browse around at the different init methods for UIImage, I think there's a different one for accepting a path.
也许, initWithContentsOfFile 不接受路径参数?浏览 UIImage 的不同 init 方法,我认为接受路径有不同的方法。
There also might be something fancier you have to do for making a path? I remember doing something with "bundles"? Sorry to be so vague, it's all I remember offhand.
您可能还需要做一些更有趣的事情来制作路径?我记得用“捆绑”做过一些事情?抱歉说得太含糊了,我只记得这些。
回答by Genericrich
take the slash off the path, and make sure it is in the project. doesn't matter if it is in that dir, but it has to be added to the project for you to access it.
去掉路径上的斜线,并确保它在项目中。它是否在该目录中无关紧要,但必须将其添加到项目中才能访问它。

