I've created a memory management demonstration project and posted it to http://rapidshare.com/files/370167478/MemoryManagement.zip
In case the link goes down, here's the source (created using "Window-based Application"):
//
// MemoryManagementAppDelegate.h
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright Trickshot Games inc 2010. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface MemoryManagementAppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
}
@property (nonatomic, retain) IBOutlet UIWindow *window;
@end
//
// MemoryManagementAppDelegate.m
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright Trickshot Games inc 2010. All rights reserved.
//
#import "MemoryManagementAppDelegate.h"
#import "MemoryManagementTest.h"
@implementation MemoryManagementAppDelegate
@synthesize window;
- (void)applicationDidFinishLaunching:(UIApplication *)application {
// Override point for customization after application launch
[window makeKeyAndVisible];
[MemoryManagementTest startTest];
}
- (void)dealloc {
[window release];
[super dealloc];
}
@end
//
// TestObject.h
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright 2010 Trickshot Games inc. All rights reserved.
//
/**
* A simple demonstration object containing an integer value.
*/
@interface TestObject : NSObject
{
int number;
}
@property(readonly) int number;
+ (id) testObjectWithNumber:(int) number;
- (id) initWithNumber:(int) number;
@end
//
// TestObject.m
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright 2010 Trickshot Games inc. All rights reserved.
//
#import "TestObject.h"
@implementation TestObject
@synthesize number;
+ (id) testObjectWithNumber:(int) number
{
// If an object creation method doesn't start with "alloc" or "new", and doesn't have "copy" in the name,
// it must be created with a pending autorelease, per Apple memory management rules.
return [[[self alloc] initWithNumber:number] autorelease];
}
- (id) initWithNumber:(int) numberIn
{
if(nil != (self = [super init]))
{
number = numberIn;
}
return self;
}
- (id) copyWithZone: (NSZone*) zone
{
// Objects created using copy must NOT be created with a pending autorelease, per Apple memory management rules.
return [[[self class] allocWithZone:zone] initWithNumber:number];
}
@end
//
// SomeContainer.h
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright 2010 Trickshot Games inc. All rights reserved.
//
#import "TestObject.h"
/**
* A singleton that contains a TestObject.
* Since this object owns the TestObject, nobody else should be releasing it.
*/
@interface SomeContainer : NSObject
{
TestObject* someTestObject;
}
/** Get the shared singleton instance. */
+ (SomeContainer*) sharedInstance;
/** Get the TestObject we're containing. */
- (TestObject*) getContainedObject;
@end
//
// SomeContainer.m
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright 2010 Trickshot Games inc. All rights reserved.
//
#import "SomeContainer.h"
@implementation SomeContainer
static SomeContainer* shared_instance;
+ (SomeContainer*) sharedInstance
{
// Very naive singleton implementation.
if(nil == shared_instance)
{
shared_instance = [[self alloc] init];
}
return shared_instance;
}
- (id) init
{
if(nil != (self = [super init]))
{
// Create and initialize our object. There is an implicit retain here because we used alloc.
someTestObject = [[TestObject alloc] initWithNumber:123456];
}
return self;
}
- (void) dealloc
{
// Remember: Anything you retained (implicitly or explicitly) MUST be released.
[someTestObject release];
[super dealloc];
}
- (TestObject*) getContainedObject
{
return someTestObject;
}
@end
//
// MemoryManagementTest.h
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright 2010 Trickshot Games inc. All rights reserved.
//
/**
* Test class demonstrating some of the gotchas in memory management.
*
* Apple has posted a guide for proper memory management in Objective-C:
* http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html
*
* Keep the guide handy as you walk through this demonstration program.
*
*
* testGoodManagement demonstrates the proper way to manage objects.
*
* testBadManagement demonstrates the wrong way to manage objects.
*
* Note: Some of the bad management tests will crash the application, so you'll need
* to comment them out in order to run through further tests.
*
* Also note: I've set NSZombieEnabled to YES in the MemoryManagement executable.
* This is good practice when debugging as it will tell you exactly what deallocated
* object you tried to use. Just remember to remove it prior to release.
* You can set it yourself by selecting "Get Info" on the executable, and adding the
* environment variable "NSZombieEnabled" with a value of "YES".
*/
@interface MemoryManagementTest : NSObject
{
}
+ (void) startTest;
- (void) testGoodManagement;
- (void) testBadManagement;
@end
//
// MemoryManagementTest.m
// MemoryManagement
//
// Created by Karl Stenerud on 10-03-30.
// Copyright 2010 Trickshot Games inc. All rights reserved.
//
#import "MemoryManagementTest.h"
#import "TestObject.h"
#import "SomeContainer.h"
@implementation MemoryManagementTest
#pragma mark -
#pragma mark Good Memory Management
- (void) testAlloc
{
TestObject* object = [[TestObject alloc] initWithNumber:1];
NSLog(@"Test object %d created using alloc (implicit retain). Retain count = %d", object.number, [object retainCount]);
[object release];
NSLog(@"Object has been released. Accessing it now would cause a crash.");
}
- (void) testAutorelease
{
TestObject* object = [[TestObject alloc] initWithNumber:2];
NSLog(@"Test object %d created using alloc (implicit retain). Retain count = %d", object.number, [object retainCount]);
[object autorelease];
NSLog(@"Test object %d still has retain count = %d, but we've added an autorelease. Retain count will decrement by 1 when control returns to the autorelease pool.", object.number, [object retainCount]);
}
- (void) testConvenience
{
TestObject* object = [TestObject testObjectWithNumber:3];
NSLog(@"Test object %d created using a convenience method. Retain count = %d, but there's also a pending autorelease. Retain count will decrement by 1 when control returns to the autorelease pool.", object.number, [object retainCount]);
}
- (void) testUseAnObjectBelongingToAnother
{
// Notice how when getting object references from convenience methods and from accessor methods you don't release.
// This is the reason for the rule about releasing objects obtained through alloc, new, and copy methods.
// You don't need to care who owns what, just follow the rule:
// - if it starts with alloc or new, or has copy in the name, release it when you're done with it.
TestObject* object = [[SomeContainer sharedInstance] getContainedObject];
NSLog(@"Test object %d belongs to someone else. It has a retain count of %d but we don't own it so we don't release it.", object.number, [object retainCount]);
}
- (void) testCopy
{
TestObject* object = [[[SomeContainer sharedInstance] getContainedObject] copy];
NSLog(@"Test object %d created by copying an existing object. Retain count = %d, and we created it using copy (implicit retain) so we must release it when we're finished with it.", object.number, [object retainCount]);
[object release];
NSLog(@"Object has been released. Accessing it now will cause a crash.");
}
- (void) testCopyAutorelease
{
TestObject* object = [[[SomeContainer sharedInstance] getContainedObject] copy];
NSLog(@"Test object %d created by copying an existing object (implicit retain). Retain count = %d, and we created it using copy so we must release it.", object.number, [object retainCount]);
[object autorelease];
NSLog(@"Test object %d (copy) still has retain count = %d, which will decrement when control returns to the autorelease pool.", object.number, [object retainCount]);
}
#pragma mark -
#pragma mark Bad Memory Management
- (void) testAllocWithMissingRelease
{
TestObject* object = [[TestObject alloc] initWithNumber:1001];
NSLog(@"Test object %d created using alloc (implicit retain). Retain count = %d", object.number, [object retainCount]);
NSLog(@"Oops! Object has NOT been released. When this method ends, all references to object will be gone, causing a memory leak.");
}
- (void) testCopyWithMissingRelease
{
TestObject* object = [[[SomeContainer sharedInstance] getContainedObject] copy];
NSLog(@"Test object %d created by copying an existing object. Retain count = %d, and we created it using copy (implicit retain) so we must release it when we're finished with it.", object.number, [object retainCount]);
NSLog(@"Oops! We forgot to release the object. When this method ends, all references to object will be gone, causing a memory leak.");
}
- (void) testAllocWithTooManyRetains
{
TestObject* object = [[[TestObject alloc] initWithNumber:1002] retain];
NSLog(@"Test object %d created using alloc (implicit retain) and a retain. Retain count = %d", object.number, [object retainCount]);
[object release];
NSLog(@"Oops! Test object %d has been released, but has retain count = %d and no pending autorelease. When this method ends, all references to object will be gone, causing a memory leak.", object.number, [object retainCount]);
}
- (void) testConvenienceWithRelease
{
TestObject* object = [TestObject testObjectWithNumber:1003];
NSLog(@"Test object %d created using a convenience method. Retain count = %d, but there's also a pending autorelease. Retain count will decrement by 1 when control returns to the autorelease pool.", object.number, [object retainCount]);
[object release];
NSLog(@"Oops! We just released it. Object has now been destroyed, but the autorelease pool still has a reference. Program will crash when control returns to the autorelease pool.");
}
- (void) testReleaseSomethingNotOursPart1
{
TestObject* object = [[SomeContainer sharedInstance] getContainedObject];
NSLog(@"Test object %d belongs to someone else. It has a retain count of %d but we don't own it.", object.number, [object retainCount]);
[object release];
NSLog(@"Oops! We just released it. Object has now been destroyed, but the actual owner of the object still has a reference. Bad things will happen soon.");
}
- (void) testReleaseSomethingNotOursPart2
{
TestObject* object = [[SomeContainer sharedInstance] getContainedObject];
NSLog(@"Got an object belonging to someone else, but this object has already been destroyed due to our earlier carelessness. Accessing this object now will cause the program to crash.");
NSLog(@"Object value is %d, but you'll never see this message because the program is now crashing.", object.number);
}
#pragma mark -
#pragma mark Test Suite
- (void) testGoodManagement
{
[self testAlloc];
[self testAutorelease];
[self testConvenience];
[self testUseAnObjectBelongingToAnother];
[self testCopy];
[self testCopyAutorelease];
}
- (void) testBadManagement
{
// Some of these tests will crash the program.
// You should run only one method at a time (comment out the rest) so that
// you can see the full effect of making the particular memory management error.
[self testAllocWithMissingRelease];
[self testCopyWithMissingRelease];
[self testAllocWithTooManyRetains];
[self testConvenienceWithRelease];
[self testReleaseSomethingNotOursPart1];
[self testReleaseSomethingNotOursPart2];
}
+ (void) startTest
{
MemoryManagementTest* test = [[[MemoryManagementTest alloc] init] autorelease];
[test testGoodManagement];
[test testBadManagement];
}
@end