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.
Comments