iOS Memory Management and ARC
Since Apple introduced Automatic Reference Counting (ARC), it has eased the life of a developer to concentrate on business logic and let ARC handle memory.
Developers who have started using ObjectiveC and creating iOS Apps recently, misinterpret ARC as garbage collection. ARC is not garbage collection as provided by JAVA and .NET runtimes, but a compile time feature that is executed somewhere between Preprocessor and compiler.
To understand ARC, a developer should first understand ObjectiveC’s “Object ownership” mechanism. The concept of “Object ownership” just implies that a class should either own and manage memory that it allocates or either delegate it to some other class. This mechanism can be visualized well by disabling ARC and using
Manual Retain Release (MRR) in iOS projects. For a detailed description of how MRR and Object ownership mechanism works, refer to the blog – “Memory Management” published by RY Press.
Now that it is clear that ARC is not garbage collection, then what exactly is it? ARC is a kind of preprocessor that executes before the compiler and virtually inserts release and autorelease statements for us. How to convert a non ARC project to utilize ARC and terms and keywords introduced by ARC are explained by Apple under “Transitioning to ARC release notes”
Working with developers who have recently started developing iOS projects I have come across few general misconceptions and mistakes that they do. So, let us touch base with some of my tips for new developers to help ARC manage memory in a better way.
Executing in background
All applications do have majority of the heavy executions done in background threads. And developers usually miss the fact that, releasing of memory for all autorelease or weak objects are deferred during background execution. Releasing memory of a weak object can also be deferred if they are created under heavy depth of looping. After which the OS tries to find an autoreleasepool to release and assign the memory back.
Therefore, an autorelease pool block should be added to all methods that are executed in the background and also to blocks of code that do heavy looping. An autoreleasepool can be added to a method as follows:
Nesting of statements
Developers have got habitual to club two to three statements together to reduce code size and fasten the development. But if even one of the nested statements is allocating memory, it results into memory over utilization. For example:
Here, in the highlighted statement, a string with value “Name 1” is created and added to an array. In terms of Manual Retain Release – during allocation, reference count of the string is set to 1 and when the object is added to the array, the reference count is incremented to 2. Now, in ARC, it recognizes the array object and handles releasing memory of the array and the objects added into it; but this will only decrement the reference count to 1 and not 0. Hence the memory will not be released.
The right way is, to separate out memory allocation statements. In the same example above, observe how the string with value “Name 2” is allocated to a variable and then added to the array. Therefore, ARC includes releasing of variable “name” reducing the memories reference to 1 and while it releases memory of the array it reduces reference count of the string to 0, thus causing the memory to be de-allocated.
Help ARC release memory
For every variable you allocate memory ARC waits until the end of the block or method to determine if it should be released. This causes unnecessary piling up of memory until all blocks or methods are called by the current code to complete before ARC releases it. Instead, we can help ARC to release the memory as soon as its requirement is completed. Observe the statements highlighted below:
Here, since we set the variables to nil, ARC knows that the use of the memory is complete and hence safely releases it.
Generated data can always be re-generated
Every screen of an application has some generated data that is specific to it. Developers usually do not release this memory while the screen is sent behind other screens. We cannot determine if the user will always come back to this screen, hence it is advisable to release all generated data in UIViewController’s viewWillDisapper() method and regenerate it when a user comes back to this screen, i.e. in viewWillAppear() method.
While any application is tested using Instruments, allocations show that the memory utilization rises as the user switches from one screen to another. The tip here is to watch for reduction of memory utilization back to what it was when the user hits back to the previous screen. So, how can we achieve this? Well, all member variables of UIViewController, responsible for a screen, should be initialized in the viewWillAppear() method and should be set to nil (help ARC release it) in viewWillDisappear().
Use of Static code Analyzer
XCode comes with static Analyzer that goes through your code and points out all potential leaks. The analyzer presents all leaks with a very helpful user interface in XCode, which helps in understanding the leak. Having a process to run Static Analyzer over each class you create and understand all issues that it detects will help you increase your code practices.
ARC is a very helpful feature provided by XCode and its capabilities can be enhanced by helping it release memory well. If you plan to include C code in your applications, you will require an advance level of memory management understanding for this and Apple’s Memory Management Programming Guide for Core Foundation will help with it.