
From Dave Mark's "Beginning iPhone Development: Exploring the iPhone SDK
Getting to the App Store
One of the most opaque bugs I've had to deal with in Cocoa is leaving a released object in the autorelease pool, causing an EXC_BAD_ACCESS in NSPopAutoreleasePool?(). When this happens, it's pretty much impossible to tell what the doubly-released object was and where it was instantiated.
Fear no more! Using Cocoa's NSZombie? debugging class and the command-line malloc_history tool, we can nail this bug in a pinch.
Suppose you have the following (obviously incorrect) code:
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSData* data = [NSData dataWithBytes:"asklaskdxjgr" length:12];
[data release];
[pool release];
The dataWithBytes: method sends an autorelease message to the created object, so we don't need to release it ourselves. When the autorelease pool is tossed the freed data object gets another release message, our app crashes, and we have no idea why.
The dataWithBytes: method sends an autorelease message to the created object, so we don't need to release it ourselves. When the autorelease pool is tossed the freed data object gets another release message, our app crashes, and we have no idea why.
Here's what we do:
Click on the "Targets" tab, open "Executables" and select the app (In XCode 2.0, double-click the executable in the file tree and select the arguments tab to enter environment variables). In the executable settings, add the following environment variables and set their values to "YES" (without the quotes):
NSDebugEnabled
NSZombieEnabled
MallocStackLogging
You may also want the following environment variable set to YES:
MallocStackLoggingNoCompact
With NSZombieEnabled, Cocoa sets an object's isa pointer to the NSZombie? class when its retain count drops to zero instead of deallocating it. Then when you send a message to an NSZombie? object (i.e., you're accessing freed data), it raises an exception and tells you where the object lives:
2003-03-18 13:01:38.644 autoreleasebug[3939] *** *** Selector 'release'
sent to dealloced instance 0xa4e10 of class NSConcreteData.
Since you have MallocStackLogging turned on, you can now run "malloc_history
[dave@host193 Frameworks]$ malloc_history 3939 0xa4e10
Call [2] [arg=32]: thread_a0000dec |0x1000 | start | _start | main |
+[NSData dataWithBytes:length:] | NSAllocateObject | object_getIndexedIvars |
malloc_zone_calloc
if you run under gdb, you may enter:
(gdb) shell malloc_history 3939 0xa4e10
And there it is: the double-released object was allocated with [NSData dataWithBytes:length:] in the function main()!
I love you, Cocoa!
The following simple examples illustrate the contrast between creating a new object using alloc
, using a convenience constructor, and using an accessor method.
The first example creates a new string object using alloc
: It must therefore be released.
- (void)printHello { |
NSString *string; |
string = [[NSString alloc] initWithString:@"Hello"]; |
NSLog(string); |
[string release]; |
} |
The second example creates a new string object using a convenience constructor: There is no additional work to do.
- (void)printHello { |
NSString *string; |
string = [NSString stringWithFormat:@"Hello"]; |
NSLog(string); |
} |
The third example retrieves a string object using an accessor method: As with the convenience constructor, there is no additional work to do.
- (void)printWindowTitle { |
NSString *string; |
string = [myWindow title]; |
NSLog(string); |
} |
Within a given block of code, the number of times you use copy
, alloc
and retain
should equal the number of times you use release
and autorelease
.
You only own objects you created using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc
, newObject
, or mutableCopy
), or if you send it a retain
message.
Implement a dealloc
method to release the instance variables you own.
You should never invoke dealloc
directly (other than when you invoke super’s implementation in a custom dealloc
method).
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html
alloc
Allocates memory for an object, and returns it with retain count of 1
.
You own objects you create using any method that starts with the word alloc
or with the word new
.
copy
Makes a copy of an object, and returns it with retain count of 1
.
If you copy an object, you own the copy. This applies to any method that contains the word copy
where “copy” refers to the object being returned.
retain
Increases the retain count of an object by 1
.
Takes ownership of an object.
release
Decreases the retain count of an object by 1
.
Relinquishes ownership of an object.
autorelease
Decreases the reference count of an object by 1
at some stage in the future.
Relinquishes ownership of an object at some stage in the future.
The rules for memory management are summarized as follows (see also “Memory Management Rules”):
Within a given block of code, the number of times you use copy
, alloc
and retain
should equal the number of times you use release
and autorelease
.
You only own objects you created using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc
, newObject
, or mutableCopy
), or if you send it a retain
message.
Implement a dealloc
method to release the instance variables you own.
You should never invoke dealloc
directly (other than when you invoke super’s implementation in a custom dealloc
method).
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html
Cocoa implements its ownership policy through a mechanism called “reference counting” or “retain counting.” When you create an object, it has a retain count of 1
. When you send an object a retain
message, its retain count is increased by 1
. When you send an object a release
message, its retain count is decreased by 1
(autorelease
causes the retain count to be decremented in the future).
When its retain count drops to 0
, an object’s memory is reclaimed—in Cocoa terminology it is “freed” or “deallocated.” When an object is deallocated, its dealloc
method is invoked automatically. The role of the dealloc
method is to free the object's own memory, and dispose of any resources it holds, including its object instance variables.
If your class has object instance variables, you must implement a dealloc
method that releases them, and then invokes super's implementation. For example, if the Thingamajig class had name
and sprockets
instance variables, you would implement its dealloc
method as follows:
- (void)dealloc |
{ |
[sprockets release]; |
[name release]; |
[super dealloc]; |
} |
You should never invoke another object’s dealloc
method directly.
In many cases you can (and should) avoid the need to implement your own accessor methods by using the Objective-C declared properties feature and asking the compiler to synthesize accessor methods for you:
@synthesize firstName; |
@synthesize fullName; |
@synthesize birthday; |
@synthesize luckyNumber; |
Even if you need to provide your own implementation, you should declare accessors using a declared property—you must ensure, of course, that your implementation meets the specification you give. (Note in particular that by default a declared property is atomic; if you don’t provide an atomic implementation, you should specify nonatomic
in the declaration.)
You should typically use the Objective-C declared properties feature to declare accessor methods (see Declared Properties in The Objective-C 2.0 Programming Language), for example:
@property (copy) NSString *firstName; |
@property (readonly) NSString *fullName; |
@property (retain) NSDate *birthday; |
@property NSInteger luckyNumber; |
The declaration makes explicit the memory management semantics for the property.
autorelease
wherever possible as it’s a more resource intensive operation than release
."To make sure it is clear when you own an object and when you do not, and what responsibilities you have as an owner, Cocoa sets the following policy:
You own any object you create.
You “create” an object using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc
, newObject
, or mutableCopy
).
If you own an object, you are responsible for relinquishing ownership when you have finished with it.
You relinquish ownership of an object by sending it a release
message or an autorelease
message (autorelease is discussed in more detail in “Delayed Release”). In Cocoa terminology, relinquishing ownership of an object is typically referred to as “releasing” an object.
If you do not own an object, you must not release it.
This policy applies both to GUI-based Cocoa applications and to command-line Foundation tools.
The entire documentation can be found here: