Objective-C: Memory Management
Automatic Reference Counting (ARC), strong and weak references, and atomicity
For every object that an application uses, memory must be allocated and subsequently deallocated to ensure that memory is being used efficiently. This is what we refer to as memory management. One of the primary goals of any memory management system is to reduce the amount of memory used at any given time. To better understand how to manage memory efficiently in Objective-C, we should probably cover the basics of our two sources of memory: the stack and the heap.
The stack is a section of memory that holds local variables, in addition to internal temporary values. When you call a method, the frame — the variables and operations that the function contains — moves briefly to the top of the stack. For the duration of that function call, the variables used are stored in the stack. After execution is complete, the frame moves off the top of the stack and the memory is deallocated. Programmers don’t take any explicit action over the stack — other than when they call a method — the operating system takes care of this automatically.
The heap is basically everything else. In Objective-C, objects are usually created on the heap by the alloc method. The [NSObject alloc] call allocates a chunk of heap memory that can fit an NSObject. When the object is no longer in use, it needs to be deallocated from the Heap to avoid memory leaks.
When you manage your memory manually, you have to retain, release, and autorelease for the duration of that object. This system of memory management is called, is called Manual Retain Release (MRR). When an object is created you own that object and were responsible for releasing it after you were finished.
//Create an object and claim ownership of it.
Orange *naval = [[Orange alloc] init];
//Claim ownership of an existing object.
//Copy an object and claim ownership of it.
//Relinquish ownership of an object and destroy
//Relinquish ownership of an object but defer its destruction
If we didn’t call the release method, we would cause a memory leak. If we release one too many times we could cause a dangling pointer. Right now we only have one object to worry about so this may not seem like a big deal, but in an application you could have hundred of objects to manage and these errors can cause your entire program to crash.
Automatic Reference Counting, or ARC, was created to combat this. ARC evaluates the references to an objects and inserts appropriate memory management calls (retain, release and autorelease) automatically at compile time. ARC keeps track of the number of existing references to an object and once that count reaches zero, deallocates the memory.
Pretty easy, right?
Don’t get too cozy — there are some instances where you might have to step in and actively manage your programs memory, like when we are dealing with properties.
When you are creating a method declaration, you will probably define some properties:
@property (nonatomic) NSString *origin;
When you define something as a property, the compiler will automatically assign a getter and setter method to that property. You’ll notice that we are defining it similarly to an instance variable except we are using the @property directive and using a keyword called nonatomic.
Nonatomic refers to a properties atomicity, or how it behaves in a threaded environment. Properties are atomic by default meaning that in a multi-thread environment the setter and getter methods cannot be interrupted by other operations (which can cause data corruption). We don’t really need to worry about multiple threads at this point, so to save some compile time we can override the atomicity to nonatomic.
There are two other variable qualifiers that we can alter at this point: whether the attribute is a strong or weak reference. These keywords allow us to manually manage how the objects assigned to these properties are handled in memory.
By default, all object properties are strong. This means that the attributes have an owning relationship to the object they are assigned. There are also weak references, which mean that relationships are non-owning and that the attribute does not keep the reference object alive. When the weak reference no longer has a strong reference object, it is set to nil.
So how do you know what should be weak and what should be strong? A use of a weak attribute is parent-child data structures. By convention, the a parent should maintain a strong reference to its children. Similarly, the children should store a weak reference back to the parent.
So, other than dealing with retain cycles, memory management is a pretty hands-off process thanks to Automatic Reference Counting. Shoutout to ARC for letting us focus on high-level functionality instead of the memory management!