Learn Objective-C on the Mac - Mark Dalrymple [125]
So, fix this by changing the string declaration line to this:
Build & Run, and off you go. It’s perfectly okay to send a message to a nil pointer in Objective-C (it’s basically a non-operation, and the return value from a nil message is typically nil, 0, or NO), so, although the fixed version of the method won’t actually do anything, it won’t cause a crash, either.
Now, there’s one more source of memory problems that we should mention here: Sending a message to an object that’s already been freed. Throughout this book, we’ve been asking you to use garbage collection for every app we’ve created, which virtually eliminates this kind of bug entirely, but in case you’re working at some point on a project without garbage collection, such as a Mac application that uses a library that won’t work with GC, or any iPhone project, it can be a good idea to know where it’s coming from.
Without GC, memory management for Objective-C objects is handled with manual reference-counting. To make a long story short: any time you create an object with an alloc or copy method, or mark an object as being “in use” by sending it a retain, its retain count is increased. You must at some point (when you’re done with the object) send the object a release or autorelease message, either of which will decrease its retain count. When the retain count hits zero, the object is freed.
The problem that can arise is that if you don’t do this quite right, you may end up in a situation where a variable contains a pointer to a space in memory that was formerly occupied by a “live” object, but now may contains a freed object, or may have been reused for some other purpose.
So let’s see this in action. When creating the ExceptionCity project, we intentionally refrained from asking you to turn on garbage collection, because it makes no difference for the rest of the code, and will actually prevent us from demonstrating this bug! So if you already went and turned on GC for this project out of force of habit (good for you!), go turn it back off. Now, add the following method to your app delegate class:
Then add a [self freedObject]; line to your applicationDidFinishLaunching: method, build & debug, and see what happens. The program will halt, and show something like the following:
As with the previous signal-generating bug, the program may receive a different signal than the one shown previously, but it’ll certainly receive something, and if you look in the call stack for the uppermost stack frame containing your code, you’ll see that it’s pointing squarely at the final NSLog() call in the freedObject method.
The way to fix this is, as with the previous case, a matter of self-discipline: any time you send an object a release or autorelease message, if you still have a pointer to that object in a variable, immediately point that variable at nil! Sending release or autorelease may not always immediately cause the object to be freed (since some other code may have increased its retain count as well), but locally within each method, you should consider anything you’ve released to be off-limits, and get rid of any dangling pointers to it as soon as you can.
In this case, the solution is to follow up the [obj release]; line with this:
Add that line, Build & Run again, and you’re back to smooth sailing! Again, this type of bug can really only occur if you’re not using GC. And, apart from this relatively simple rule of thumb about setting pointers to nil after releasing objects, there are other ways that a non-GC app can wind up in similar situations, such as forgetting to retain an object that you want to use for a while. These bugs can be some of the trickiest to track down, which is one of the strongest reasons for using GC wherever possible.
NSError
Now you know that exceptions in Cocoa