Memory Management in Objective-C
Objective-C provides several memory management mechanisms. Understanding these is crucial for preventing memory leaks and crashes in your applications.
Memory Management refers to the process of allocating and deallocating memory for objects during program execution.
Manual Reference Counting (MRC)
// Manual memory management (pre-ARC)
NSObject *obj = [[NSObject alloc] init]; // retainCount = 1
[obj retain]; // retainCount = 2
[obj release]; // retainCount = 1
[obj release]; // retainCount = 0, object deallocated
// Autorelease pools
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *temp = [[[NSString alloc] initWithFormat:@"Hello %@", @"World"] autorelease];
[pool drain]; // temp is released when pool is drained
Important: Under Manual Reference Counting, you must balance every alloc
, retain
, or copy
with a corresponding release
or autorelease
.
Automatic Reference Counting (ARC)
// With ARC (modern Objective-C)
NSObject *obj = [[NSObject alloc] init]; // Automatically managed
NSString *str = [NSString stringWithFormat:@"Value: %d", 42];
// Weak references to prevent retain cycles
__weak NSObject *weakObj = obj;
// Strong references (default)
__strong NSObject *strongObj = obj;
// Unsafe unretained references
__unsafe_unretained NSObject *unsafeObj = obj;
ARC Ownership Qualifiers
Qualifier | Description |
---|---|
__strong |
Default, keeps object alive |
__weak |
Doesn't keep object alive, auto-nils when deallocated |
__unsafe_unretained |
Like weak but becomes dangling pointer when deallocated |
__autoreleasing |
For passing objects by reference to methods |
Memory Management Rules
- You own any object you create with
alloc
,new
,copy
, ormutableCopy
- You can take ownership using
retain
- When you no longer need an object you own, you must relinquish ownership with
release
orautorelease
- You must not relinquish ownership of an object you don't own
Retain Cycles
// Retain cycle example
@interface Parent : NSObject
@property (strong, nonatomic) Child *child;
@end
@interface Child : NSObject
@property (strong, nonatomic) Parent *parent; // Creates retain cycle
@end
// Solution: Use weak reference
@interface Child : NSObject
@property (weak, nonatomic) Parent *parent; // Weak breaks the cycle
@end
Warning: Retain cycles are a common source of memory leaks. Always use weak references for parent-child relationships.
Autorelease Pools
// Modern @autoreleasepool syntax
@autoreleasepool {
for (int i = 0; i < 100000; i++) {
NSString *temp = [NSString stringWithFormat:@"Object %d", i];
// temp is autoreleased
}
// All autoreleased objects are released here
}
// Nested autorelease pools
@autoreleasepool {
// Outer pool
@autoreleasepool {
// Inner pool
NSString *inner = [NSString stringWithFormat:@"Inner"];
}
// inner is released
}
Dealloc Method
- (void)dealloc {
// Release resources, remove observers, etc.
[_observable removeObserver:self];
[_timer invalidate];
// Under MRC:
[_resource release];
[super dealloc];
// Under ARC, just clean up:
_resource = nil;
}
Note: In ARC, you rarely need to implement dealloc
except to clean up non-Objective-C resources (file handles, observers, etc.).
Memory Management in Collections
// NSArray retains its objects
NSMutableArray *array = [NSMutableArray array];
NSObject *obj = [[NSObject alloc] init];
[array addObject:obj]; // array retains obj
// Under MRC:
[obj release]; // Now only array retains obj
// When array is released or object removed:
[array removeObject:obj]; // obj is released by array
Debugging Memory Issues
Tools and techniques:
- Instruments: Leaks and Allocations instruments
- Static Analyzer: Built-in Xcode analyzer (⌘⇧B)
- NSZombieEnabled: Detect messages to deallocated objects
- MallocStackLogging: Track allocation origins
# Enable zombies in Xcode scheme
# Edit Scheme -> Run -> Diagnostics -> Enable Zombie Objects
Best Practices
- Use ARC for new projects
- Use weak references for delegates and parent references
- Use
@autoreleasepool
blocks for memory-intensive loops - Clean up observers and notifications in
dealloc
- Use the static analyzer regularly
- Profile with Instruments before release
- Follow naming conventions (methods starting with "new", "alloc", etc. return retained objects)
Important: Even with ARC, you can still have memory issues like retain cycles and excessive memory use. Always profile your app's memory usage.
Converting Between MRC and ARC
// Converting files to ARC
#if !__has_feature(objc_arc)
// Manual memory management code
- (void)oldMethod {
NSObject *obj = [[NSObject alloc] init];
[obj release];
}
#else
// ARC code
- (void)newMethod {
NSObject *obj = [[NSObject alloc] init];
// No release needed
}
#endif
Xcode can automatically convert projects to ARC using Edit → Convert → To Objective-C ARC...
Next: Categories & Extensions in Objective-C