Help with memory management
I'm learning Objective C having come from assembler, C, and Python.
I've discovered a memory management weirdness:
Given a class Head, and a class Human defined elsewhere:
// 1) this leaves a permanent hanging retain count...
human.head = [[Head alloc] init];
// 2) whereas this does not...
Head *head = [[Head alloc] init];
human.head = head;
[head release];
// Will 1) ever get cleaned; i.e. is it assumed to autorelease at some point?
Many thanks - especially if this is a really stupid question.
This is not a stupid question. Memory in Management in Obj/C is a very serious (and boring) matter.
If you want to use autorelease, you'll have to provide a pool (and drain). For example:NSAutoreleasePool* pool = [[NSAutoreleasePool alloc]init];
...
human.head = [[Head alloc]init];
...
[pool release];
I can only imagine the pool hooks the default alloc method of NSObject and thereby knows about every allocated object. pool release free's all the objects which have not been dealloc'ed while that pool was 'the active pool'.
If you do your own release (as you've done in 2), this is harmless because the pool knows the object has gone and doesn't free it again.
XCode 3/ObjC 2.0/Leopard introduced a garbage collector to spare you of this burden.
I've written the following code (and a command-line program with XCode 3.2.1) to try to demystify this.
I'll be honest, I'm rather puzzled by this. If I comment off [brain release], it appears as though the [pool release] cleans memory without calling Head::dealloc - in other words it doesn't appear to have done anything with the dangling head! (my guess is that [pool drain] calls [NSObject dealloc] for every object and not [Class dealloc].
Maybe another reader can enlighten us all.
#import <Foundation/Foundation.h>
@interface Head : NSObject {
}
@end
@implementation Head
- (id) init {
[super init] ;
NSLog(@"Head::init %@ retainCount = %d",self,[self retainCount]);
return self ;
}
- (void) release {
int count = [self retainCount] - 1 ;
NSLog(@"Head::release %@ retainCount after = %d",self,count);
[super release] ;
}
- (void) dealloc {
int count = [self retainCount] - 1 ;
[super dealloc] ;
NSLog(@"Head::dealloc %@ retainCount after = %d",self,count);
}
- (id) retain {
[super retain] ;
NSLog(@"Head::retain %@ retainCount = %d",self,[self retainCount]);
return self ;
}
@end
int main (int argc, const char * argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// insert code here...
NSLog(@"Hello, World!");
Head* head = [[Head alloc]init] ;
Head* brain = [head retain];
NSLog(@"",head,brain);
[brain release];
[head release];
NSLog(@"Goodnight cruel world - let's pull the plug");
[pool drain];
NSLog(@"last word after draining the swamp");
return 0;
}
You add stuff to the pool using the autorelease command:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
Object *instance = [[[Object alloc]init]autorelease];
// …
[pool drain];
If the retainCount is not incremented, instance is released with the pool. Replace the comment with the following lines:
NSLog(@"Retain count = %i", [instance retainCount];
[myObject retain];
After the drain command, add these lines: