How Manual Memory Management works in Objective C?
We will cover explanation of:
You can mostly avoid Manual Reference Counting(MRC) by turning on Automatic Reference Counting (ARC) or garbage collection.Its up to you, but Apple pushes developers to use ARC.
We will be covering:
- Coming From C/C++ -> In C/C++, you can create objects as local (a.k.a stack-allocated) variables, where allocation and deallocation is handled for you automatically. You don’t have this luxury in Objective-C. In Objective-C all objects are allocated on the heap. It is the equivalent of always using
newto create objects in C++. As a result, all object variables are pointers. In C++, an allocated bit of memory usually has a single owner. The owner deallocates the memory when appropriate. This is also slightly different in Objective-C. Objects can have multiple owners by default. You can’t say “deallocate this now.” Instead, you say “I’m not using this anymore” and the deallocation of an object happens automatically once all owners have said they are no longer using the object.
- Coming From a Garbage Collected Language (Java, Python, C#, etc.) -> In a garbage collected language, you create new objects whenever you want. Then, when you’re finished with the objects you just stop using them and forget about them. The garbage collector will magically come along and free up the memory that the objects were occupying. This is not so, in Objective-C. In Objective-C, when you create a new object you are responsible for freeing up (a.k.a. deallocating) the memory once you’re done with it. If you forget this, then the object stays in memory until the program exits. This is called a memory leak. If you keep leaking memory, you will see that the program will crash. Conversely, you also have to be careful that you don’t use an object that has been deallocated. If an object gets deallocated, but if you still points a variable pointing to that object, your app will crash. This is called a dangling pointer.
- What Is Reference Counting? -> Every object in Objective-C uses reference counting to manage its lifecycle.Reference counting allows a single object to have multiple “owners”, and ensures that the object will stay alive as long as there is at least one owner. Once the last owner is gone, the object deallocates itself.Reference counting is very simple, and works like this:
- Every object has a “reference count”, which is just an integer.
- The reference count starts with 1 when the object is created, and whoever created the object automatically has ownership of it.
- If you want to take ownership of an object, you increase the reference count of the object by one.
- When you release ownership of an object because you don’t need it any more, you decrease the reference count by one.
- When the reference count of an object reaches zero that means nobody is using it, so it is safe to be deallocated.Let’s look at an example of correct usage, using a reference counted television object.
Action Change Reference Count Jerry wants to watch Seinfeld, so he creates a TV object. Creating the object makes him the only owner. 1 Elaine wants to watch the same TV object as Jerry, so she also takes ownership of the object. +1 2 Jerry gets bored of the Seinfeld episode he is watching and decides to leave, so he releases ownership of the TV object. -1 1 The episode of Seinfeld finishes, so Elaine releases ownership of the TV object and leaves. -1 0
Now let’s look at an incorrect usage, which causes a memory leak:
Action Change Reference Count Ned wants to watch Game of Thrones, so he creates a TV object. Creating the object makes him the only owner. 1 Joffrey wants to watch the TV with Ned, so he also takes ownership. +1 2 Joffrey beheads Ned. 2 Joffrey leaves, and releases ownership of the object. -1 1 The TV object is never deallocated because the reference count will stay at one forever. 1
This memory leak occurs because Ned forgot to release ownership of the object before he was beheaded.
Now let’s look at another incorrect usage that causes a dangling pointer:
Action Change Reference Count Simon wants to watch Grandma’s House, so he creates a TV object. Creating the object makes him the only owner. 1 Adam starts watching too, but doesn’t bother taking ownership. 1 Simon releases ownership and leaves. -1 0 The reference count has hit zero, so the TV object deallocates itself. Adam tries to keep watching, but the TV object is gone so the universe explodes.
- How Does Reference Counting Work In Objective-C? -> Memory management in Objective-C involves four basic rules. If you follow these rules, then you will not leak memory or cause dangling pointers.
Rule 1. If you create an object using a method that starts with “alloc”, “copy” or “new”, then you own it.
This is where you have created a new object with a retain count of one, which automatically makes you the owner.
NSString* iOwnThis1 = [[NSString alloc] initWithString:@"hello"]; NSString* iOwnThis2 = [someOtherString copy]; NSMutableString* iOwnThis3 = [someOtherString mutableCopy]; NSString* iOwnThis4 = [NSString new]; [iOwnThis1 release]; [iOwnThis2 release]; [iOwnThis3 release]; [iOwnThis4 release];
Rule 2. If you
retainan object, then you own it.
This is where you call
retainon an object, which increases the retain count by one.
[donkey retain]; [eagle retain]; [eagle retain]; [eagle retain]; [donkey release]; [eagle release]; [eagle release]; [eagle release];
Note that you can take ownership of an object more than once. If you own the object three times, then you have to release it three times.
Rule 3. If you own it, you must release it.
This is where you call the
releasemethod on an object, which decreases the retain count by one. When you call
releaseand the retain count reaches zero, the object will deallocate itself by calling
dealloc. This means you should never call
deallocdirectly. Just release the object correctly, and it will handle everything itself.
NSString* iMadeThis = [[NSString alloc] init]; // Rule 1 [iMadeThis release]; [imSharingThis retain]; // Rule 2 [imSharingThis release]; //can own the same object many times Pidgeon* pidgeon = [[Pidgeon alloc] init]; // Rule 1 [pidgeon retain]; // Rule 2 [pidgeon release]; [pidgeon release];
Rule 4. If you keep a pointer to an object, then you must own the object (with some rare exceptions).
Basically, if you own an object, then you know it is definitely safe to use. If you don’t own it, then it is sometimes safe to use temporarily (discussed in the autorelease section). If you want to keep an object to use later, such as storing it in an ivar or a global, you must retain it. Otherwise, it might be deallocated and you will be left with a dangling pointer.
One exception is the use of strings you type directly into the code (string literals). String literals are never deallocated, and
releasedon’t do anything to them.
- Common Mistakes
So Apple developers had to manually manage memory while writing Objective-C code called the Manual Retain-Release model, and is exactly what developers have been doing all this while for C/C++.
But managing memory manually has always been painful and error prone, and takes up valuable time of application developers, which could instead be used for building useful features.
Enter ARC, acronym for Automatic Reference Counting.
Apple introduced ARC in 2011 along with Max OS X Lion and iOS 5.
With ARC, Apple has tried to make the compiler mimic what disciplined C/C++ developers would do. They would manually track the memory they are allocating for their code, and then ensure they release that memory whenever the objects go out of scope, and their memory is no longer required. So instead of the developers, now the compiler is doing all the hard work.
Its not like ARC was entirely Apple’s creation, Reference Counting has been around for a long time, and is one technique of implementing Garbage Collection.
How is ARC different from a Garbage Collector?
— Is part of the runtime infrastructure – i.e. part of CLR, JVM, and the runtime is responsible for running and monitoring the Garbage Collector (GC)
— Is indeterministic, so objects stick around until the GC runs, and therefore are not released as soon as they go out of scope
— Affects application performance when it runs, as other threads are paused, while it is runnning
— Is able to clear entire Object graphs, as it is able to figure out the application root object references while the application is running
— Is less prone to memory leaks
— It is not part of the runtime
— Instead the compiler injects reference tracking and clean-up code, similar to what a developer would do
— Deterministic reclamation of memory, at the time when the object goes out of scope
— Since there is no background or runtime processing, it requires less CPU and RAM, making it efficient on mobile devices
— Cannot cope with retain cycles/object graphs – gets stuck with an object graph, whereas a GC would look for an external reference to an object graph, and if not found would clear up the entire object graph
— Is more prone to memory leaks based on the quality of code written
A Retain Cycle is created when two or more objects are referencing each other in a circular fashion. For e.g. when we have a Parent – Child relationship between two objects, both instances would be referencing each other until they both go out of scope. This creates a circular reference and ARC is not able to figure out when to release these objects as it does not do a stack walk which a GC would do to find external references to these objects, instead ARC just tracks the count of the references to a particular object.
To solve this issue, Apple came out with Strong and Weak references. The Parent object creates a Strong reference to the Child object, which means that until the Parent object is deallocated, the Child object needs to remain alive. And the Child object creates a Weak reference to the Parent object, which breaks the circular reference cycle, and now the Parent object can be cleared whenever it goes out of scope, which in turn will also clear the Child object.
However, for the scenario above, developers need to handle missing or nil Parent objects in the Child object implementation, for when the Parent object gets deallocated and before the Child object is cleared, Child object code might be executed.
There is a concept of unowned references as well to prevent retain cycles, which I left out for now.
LLVM and Clang
Apple has used the LLVM compiler toolset to implement ARC.
“The LLVM Project is a collection of modular and reusable compiler and toolchain technologies”
The aim of LLVM is to provide a common backend compiler infrastructure to different compiler front-ends for different languages.
At a high level LLVM accepts code in an Intermediate Form and then is able to emit machine code based on the target architecture.
Ultimately we should be able to write code in different programming languages, run it through LLVM to generate native code for different platforms.
Apple developed the Clang frontend for C, C++, Objective-C and now Swift to use with LLVM. Clang is where the ARC magic happens.
Earlier Apple was using the GCC compiler for Objective-C and XCode, however as GCC is old and has a massive codebase to allow easy addition of new features and advancement, Apple decided to switch to the LLVM toolset. GCC also uses the GPL license, which would have forced Apple to GPL XCode’s source whereas LLVM uses the more lenient BSD license to protect proprietary software.
This was a short introduction to how iOS uses ARC to manage its memory, and about the tools that are used for ARC, do send in your feedback via comments. In future, I am planning to write a post on the tools which are available with XCode, which help diagnose memory related issues.