Objective-C Memory management
reference-counting mechanism is implementation of object ownership
technic for Memory management
MRC, ARC, GC
Manual Reference Counting(MRC)
or Manual Retain-Release(MRR)
as a developer you are responsible for counting references on objects manually
Automatic Reference Counting(ARC)
was introduced in iOS v5.0 and OS X Mountain Lion with xCode v4.2
Garbage Collection(GC)
was available for Mac OS and was deprecated in OS X Mountain Lion. Must Move to ARC
Reference count in MRC and ARC
//MRC
NSLog(@"Retain Count: %d", [variable retainCount]);
//ARC
NSLog(@"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));
Every object in heap has an integer value which indicates how many references are pointed out on it. When it equals to 0 object is deallocated by system
Lifecycle:
- Allocating object
- Working with Reference count
- Deallocating object.
deinit
is called when retainCount == 0
MRC
A *a1 = [[A alloc] init]; //A has retainCount = 1
A *a2 = a1;
[a2 retain]; //A has retainCount = 2
// a1, a2 -> reference on the same object in heap with retainCount = 2
Correct way to release an object:
release
. If use only this technic - dangling pointer. Because it still can point on the object in heap and it is possible to send a message. For example A makes +1, B just make a reference(without +1), A makes release -1, counter == 0, object is deallocated but B has a reference to heap
= nil
. If use only this technic - memory leak. deinit
will not be called. For example when counter == 1 and you make = nil
. In any case when you forget release unused object where counter != 0 it causes memory leak
A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped
Working with Reference count(Object owner rules):
- (0 -> 1)
alloc
, new
, copy
, mutableCopy
- (+1)
retain
You are able to own an object as many times as you need(you can call retain
several times)
- (-1)
release
If you an owner you must release it. If you release more than retainCount it will be 0
- (-1)
autorelease
Adds an object, which should be released, to autorelease pool
. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release
will be applied for all objects in the pool
- (-1)
@autoreleasepool
Forces process an autorelease pool(release objects) at the end of block. It is used when you deal with autorelease
in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing
For example autorelease
is helpful in method calls when you allocate a new object there and return it
- (B *)foo {
B *b1 = [[B alloc] init]; //retainCount = 1
//correct way
[b1 autorelease];
//wrong way is to return b1 without putting it into autorelease beforehand
return b1;
}
- (void)testFoo {
B *b2 = [a foo];
//if autorelease was not used in foo
[b2 retain]; //retainCount = 2
//some logic
[b2 release]; //retainCount = 1
//Memory Leak
}
@autoreleasepool
example:
- (void)testFoo {
@autoreleasepool {
for(i=0; i<100; i++) {
B *b2 = [a foo];
//process b2
}
}
}
ARC
One of biggest advantage of ARC
is that it automatically insert retain
, release
, autorelease
under the hood in Compile Time and as developer you should not take care of it anymore
Enable/Disable ARC
//enable
-fobjc-arc
//disable
-fno-objc-arc
Variants from more to less priority
//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter)
//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)
//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)
Check if ARC is enabled/disabled
Preprocessor
__has_feature
function is used
__has_feature(objc_arc)
Compile time
// error if ARC is Off. Force to enable ARC
#if ! __has_feature(objc_arc)
#error Please enable ARC for this file
#endif
//or
// error if ARC is On. Force to disable ARC
#if __has_feature(objc_arc)
#error Please disable ARC for this file
#endif
Runtime
#if __has_feature(objc_arc)
// ARC is On
NSLog(@"ARC on");
#else
// ARC is Off
NSLog(@"ARC off");
#endif
Reverse engineering(for Objective-C)
//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments" | grep "_objc_release"
//result
0x00000001000080e0 748 _objc_release
//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong
Tool to Migrate Objective-C MRC to ARC
ARC generates errors where you should manually remove retain
, release
, autorelease
and others issues
Edit -> Convert -> To Objective-C ARC...
New Xcode with MRC
If you enable MRC you get next errors(warnings)(but the build will be successful)
//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'