Tuesday, March 30, 2010

EXC_BAD_ACCESS and Memory Management.

Hello,

I know I haven't said much for a bit, but I also have no followers yet, so hopefully none of them will mind too much.

I had to post this cause of how hilarious it is if you know what EXC_BAD_ACCESS means. This Apple Support discussion is between a few people who clearly know a bit about programming, and a bunch of people who really don't have a clue and don't particularly want one.

They are insistent that apple has a bug called EXC_BAD_ACCESS and they really need to patch it cause it seems to affect a lot of different programs.

If you don't see the significance of all this let me explain it a bit:

EXC_BAD_ACCESS happens when a program tries to change memory it does not have access to.

When you program for the iPhone, you use up memory by creating objects using alloc. Any time you create an object, for example

NSString *myString = [[NSString alloc] init];

the object gets a retain count of 1. See, objects in Objective-C / iPhone are sad individuals. They keep a little book of everyone who cares about them, well not a book really, just a count (retain count) adding and subtracting people (objects) as they are notified, and if their count ever gets to zero, they erase themselves. This way nobody has to tell the object to go away, they just tell it when they are done with it, and if everybody is done, the object gets erased, and the memory gets freed.

This is a great system, if you use it correctly. The problem is sometimes people forget to tell object they care (or don't care).

When you create an object with alloc, that means you care. The object gets a retain count of 1. (it sends itself a message [myObject retain]; . Now later on, if you send it a message [myObject release]; the object subtracts 1 from the list and offs itself. Memory freed.

If you create an object without alloc, for example myObject = [NSString stringWithString: @"Hello!"];

the object doesn't start out with that retain count. It is "autoreleased". This means it wrote it's name on a list (called the autorelease pool) and as soon as the run loop iterates (meaning within a couple milliseconds after your function was called and finished performing what it was supposed to do) everyone on the list (autorelease pool) gets a release message.

You can send these autorelease messages yourself if you want: [myObject autorelease] and your objects will get a release message unless somebody sends them retain.

So now let's say for example that you created a new object with alloc, and you didn't send it a
release message, then your variable went out of scope.
{
//anything that is declared in these braces goes out of scope when the braces end
NSString *myString = [[NSString alloc] init];
//myString is in scope here
}
//out of scope here
NSLog(@"%@", myString); //doesn't work.

Once you are out of scope, the word myString is meaningless. You don't have any way to access the NSString that you alloc'd anymore. The problem is, you didn't tell the string you had stopped caring, so the string didn't free up its memory.

Remember watching the matrix reloaded? I know, we've all tried to forget, but here's a quote: "Every program that is created must have a purpose, if it does not it is deleted."

So if you forget to tell an object you don't care, it won't be deleted, and eventually you'll use up all the memory, and your program will crash. This doesn't cause EXC_BAD_ACCESS though, that is a different problem.

If you instead create an object you do care about, but by some mistake, either not using alloc to create it, or sending it a release message too many times, the object erases itself. Then when you go to look for it it isn't there, but it's more than happy to haunt you cause you didn't tell it you cared: it causes an EXC_BAD_ACCESS. Then your program crashes, and you get no useful information on why it crashed from the error message.

So the moral of the story is: Tell them you care too much and they won't leave, (memory leaks) don't tell them enough and they haunt you when you go looking for them later on (exc_bad_access). This isn't the whole picture, I will go into more detail at some point, or if I get comments asking, but that's the general idea.

alloc retain count is 1
retain retain count is 2
autorelease retain count will drop by 1 after the run loop iterates
release retain count is 1
...
run loop iterates -- retain count drops to 0
object calls dealloc on itself, and frees the memory.

If you have the EXC_BAD_ACCESS error, search up how to enable zombies, it is useful in finding the source of the problem.

I'm forgetting... the point is that EXC_BAD_ACCESS isn't caused by apple, it is caused by the programmer. It isn't a single bug, it is many different bugs.

Peace

1 comment:

  1. I'm five years late, but this was a nice explanation, thanks!

    ReplyDelete