Monday, April 17, 2017

Memory Management in Objective-C

Note: This article was written a very long back in the period of Xcode 6.

Any application that runs on a device needs some sort of space in memory(RAM) during runtime in order to store any necessary data to be displayed or handled in the app. Any program that runs on the device needs to manage their memory based on the system resources(RAM Memory) by controlling or managing the lifetime of all the objects created for the app.
iOS Applications does this through a process called 'Object Life Cycle Management' Or Otherwise called as 'Object Ownership'.
This Ownership scheme is handled through a 'Reference Counting' mechanism, which uses a tracking mechanism internally to detect the number of owners for each object.


Reference Counting Rules:
  • Reference Count = 1, when an object is created and the Creator is the owner here.
  • Reference Count +1, whenever a new owner is added.
  • Reference Count -1, whenever an owner releases its reference.
  • When Reference Count == 0, then the object is destroyed by automatically calling the dealloc() method. You never have to call the dealloc() manually, calling release on an object does it automatically.

Memory Management in Objective C is basically defined as “the process of allocating memory during your program’s runtime, using it, and freeing it when you are done with it”.

There are 2 ways to accomplish it:
  • MRR (Manual Retain Release)
  • ARC (Automatic Reference Count)



MRR (Manual Retain Release):
iOS Version: iOS 4.3 and below
Xcode Version: 4
Using this method you can explicitly manage your memory by keeping track of all the objects you own using the Reference Counting mechanism.


ARC (Automatic Reference Count):
iOS Version: iOS 5 and above
Xcode Version: 4.2
This method also uses the Reference Counting mechanism, but does it automatically by inserting the memory management method calls on behalf of you at the compile time. Thus, ARC uses MRR behind the bars and handles the dirty stuff of managing the memory for you.


So, before iOS5 was introduced, developers used MRR to manually manage their memory to use the system resources efficiently.
Even after iOS5 and Xcode4.2, Apple provided its developers with the ability to use the MRR method by setting a Boolean value in Build Configuration of the project to “Objective C Automatic Reference Counting”. Hence setting it to Yes means, the app uses ARC and NO means, the app uses MRR method.



This is a screenshot of how to set the variable of Memory Management method in Xcode6:





Let's take a look at the MRR method.



MRR (Manual Retain Release):

In Manual Retain Release method, it is completely your own responsibility to claim and relinquish the ownership of any object you create in your program, which can be done by the following memory management methods:

Method
Behaviour
Alloc
Creates an object and claims ownership of it.
Retain
Claims ownership of an already existing object.
Copy
Copies an object and claims ownership.
Assign
Assigns an object without owning it.
Release
Relinquish ownership of it and destroys it immediately.
Autorelease
Does same as Release, but postpones it after its use.



In Objective C, an object is Created or Initialised as follows:

NSString *string = [[NSString alloc] initWithFormat:@”String Variable holding content”];

When an object is initialised with any memory management methods(alloc, copy, retain), it is not just created but it's Reference Count is also increased by 1.
Thus, the above 'string' object now has a Reference Count of 1.

Now, if the same object is retained as follows:

[string retain];

Now, the Reference Count is increased to 2.
Thus, it needs to be released twice to destroy the 'string' object completely from the memory, which could be done as follows:

[string release];


The basic idea behind the MRR method is that, you need to balance between the memory allocation and deallocation methods in order to free up the memory.
Suppose, if you forget to deallocate the object, then the object remains there until your application quits, thus it means there is a “Memory Leak”. It wont have any severe effect, if the object occupies tiny space, but if your program keeps on doing this spontaneously, then the system may run out of memory and your application might crash.

Also, if you try to release an object too many times, which is called as “Dangling Pointer”, means the pointer of an object refers to an invalid memory address, since the object is already deallocated thus freeing up from the memory. This situation means that your program will most likely crash.

Let's have a look at an example:

{
Person *aPerson = [[ Person alloc] init];
// ...
NSString *name = aPerson.name;
// ...
[aPerson release]
}

Here, since we create an object for Person model/class, we take ownership of it, hence we deallocate/release it after its use. Whereas, we don't take ownership of the string object, hence we don't release it.
When you don't release 'aPerson' object, then it creates a memory leak, which could be detected by using the tools provided within Xcode. Just tap on Product --> Analyze, you will be pointed to the exact line of code of where it causes the leak.


dealloc() Method:
When we release an object, dealloc() method is called and thus we can release the instance variables of any custom classes as follows:

@interface Person : NSObject
@property (retain) NSString *name;
@property (retain) NSString *age;
@end

@implementation Person
- (void)dealloc
{
[_name release];
[_age release];
[super dealloc];
}
@end

dealloc() method releases and frees up the memory of those instance variables.
Also, you need to call the dealloc method of the super class.

Explaining Assign:

Let's have a look at the following example:

We have two models:

// Car.h
#import
#import "Person.h"
@interface Car : NSObject

@property (nonatomic) NSString *model;
@property (nonatomic, retain) Person *driver;

@end



// Person.h
#import

@class Car;

@interface Person : NSObject

@property (nonatomic) NSString *name;
@property (nonatomic, retain) Car *car;

@end


@class Car, says the compiler that there is a class called Car that already exists, thus it wont keep importing the class again & again, since we have already imported Person class in Car.


In the main.m class:

// main.m
#import
#import "Car.h"
#import "Person.h"

int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *john = [[Person alloc] init];
john.name = @"John";
Car *honda = [[Car alloc] init];
honda.model = @"Honda Civic";
honda.driver = john;
NSLog(@"%@ is driving the %@", honda.driver, honda.model);
}
return 0;
}


This says that i have initialised a Person object and assgined it to the driver of the Car object, which means Car object holds an ownership of Person object.

Now, add this line:
john.car = honda;
after this line:
honda.driver = john;
in main.m class, which means that the Person object also owns the Car object, which means that both the objects owns each other and the memory management system wont be able to destroy them even if they are no longer needed. This is called as the “Retain Cycle”, which is a bad memory leak.

Fortunately, it is also easy to fix by just setting a weak reference to any of the properties holding back the other object.


So, now let's do it for the Car object in Person class as follows:
@property (nonatomic, weak) Car *car;

Now, the Person object has a non-owing relationship to the Car object.


Thus, the basic idea behind the bar is that No 2 objects must have retained relationship when they are linked to each other, thus avoiding the Retain Cycle.



ARC (Automatic Reference Count):
ARC introduces new @property attributes. You should use strong in case of retain and weak in case of assign.
You don't have to keep/maintain the Reference Count of any objects, and all the memory management methods are inserted into the code during the compile time on your behalf.

dealloc() method:
You don't need to release the instance variables and call the super class's dealloc() method as we did in MRR method, which is automatically done for you by ARC.
That means there is no need for you to include the dealloc() method in the class at all.


Summary:

To develop modern applications, developers must choose ARC to leave the headache of Memory Management to the system and thus focus on the app features in order to get the best out of it.
ARC handles all the memory management stuff except that you have to play carefully with the Retain Cycles.

No comments: