Thursday, March 3, 2011

Memory Management


In this post we will have a look on how to manage memory in the iPhone.

Just like the mac os the iphone does not have something called as garbage collector, the iPhone has its own mechanism to manage memory and that mechanism is called as the Retain count algorithm.

Working of the retain count algorithm: The retain count algorithm has one basic principal and that’s
“If you own an object you are responsible to free the memory occupied by that object once it’s use is over”

You free the memory occupied by the object with the help of the line given below

[ObjectName release];


The function release says that “I will release only those objects from the memory whose retain count is zero”, 
you can check the retain count of an object with the help of the function called as the retainCount

NSLog(@"The retain count is %d",[emp retainCount]);


Now you must be thinking that how do I own an object, well you own an object if you do the following in your code

1.     Alloc and init memory for your object.

Employee *emp = [[Employee alloc]init];


2.     If you retain an object

[emp retain];


By making an explicit call to the retain what you are doing is that you are telling the iPhone os that I don’t want to release this object now and want to keep this object in the memory for sometimes and then release it

3.     Also if you copy the object using the copy function.

So now once the above part is clear we can move to the coding part where we can see the above things working

Step 1: Open Xcode and select the command line utility template and select foundation tool



Add a NSObject subclass file to it with the name Employee, now you can see that there are two files which are added in your application one is the Employee.h and the other is the Employee.m file.




In the Employee.h file write a function named doSomething

@interface Employee : NSObject {

}
-(void)doSomething;

@end


And in the Employee.m file give that function (doSomething) a body and write any piece of code you like here’s a view at my code

@implementation Employee

-(void)doSomething
{
NSLog(@"doSomethng called");
}


@end


Now select the main.m file from your project and alloc init memory for your class.




#import <Foundation/Foundation.h>
#import "Employee.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Employee *emp = [[Employee alloc]init];
[emp doSomething];
        [pool drain];
    return 0;
}



In the above line alloc says that I am gona allocate a block of memory for this class, whereas init says that  I will initialize the variables and functions in the allocated block of memory by the alloc.

Press build and go to see the output in your console.



Step 2: Nothing much fancy about the above code like we have already done that in our previous tutorials when I used to explain you what are the basics of objective c.
Now we are going to see the memory management part starting from retain count
Just add the line

NSLog(@"The retain count of emp after alloc init is %d",[emp retainCount]);


Before the function call and then have a look at the retain count




The retain count is one this is because you have used alloc and init in your code and took ownership of that object and whenever you alloc and init the object the retain count automatically increments by one.

Now in order to make this even more cool we will increase the retain count of our object and then see what count we get

#import <Foundation/Foundation.h>
#import "Employee.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Employee *emp = [[Employee alloc]init];
NSLog(@"The retain count of emp after alloc init is %d",[emp retainCount]);
[emp retain];
NSLog(@"The retain count of emp after retain is %d",[emp retainCount]);
    [pool drain];
    return 0;
}




We can see that the retain count increased by one and hence we can see the count two over their, by passing a retain message to your object you are telling the iphone os that I want to maintain the copy of this object in the memory.

Step 3: Now we will see how to pass the release message to the object.

Employee *emp = [[Employee alloc]init];
[emp doSomething];
[emp release];

By passing the release message to the object you specify the iPhone os that “ok I am done with this object and its of no use to me you can clean it up”

And if you try to access a function after the release 

Employee *emp = [[Employee alloc]init];
[emp doSomething];
[emp release];
[emp doSomething];


your application will go south.



In the above step no 2 the retain count of emp is two, right so in this case I have to pass the release message twice to emp in order to free the space occupied by it.

And even if you try to access the function doSomething after the first release then it will work perfectly because we have explicitly said to the iPhone os that I don’t want to release this instance now, with the help of retain message that we passed earlier. Here's the code and the output snap for that

Employee *emp = [[Employee alloc]init];
[emp doSomething];
[emp retain];
NSLog(@"Retain count after retain is %d",[emp retainCount]);
[emp release];
NSLog(@"Retain count after release is %d",[emp retainCount]);
[emp doSomething]; //calling doSomething after release....





Step 4: This is how you manage memory with the help of Retain count algorithm, there is also another method with the help of which you can do the same, just have a look at the line given below

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


This line creates a pool of memory for your object, the object that you create can be added to this pool with the help of the code given below.

Employee *emp = [[Employee alloc]init];
[pool addObject:emp];//adding emp to pool...

[emp doSomething];

[pool drain];


Also you can see the line which passes a message called as drain to the pool, well what this message says that “I am going to free all the objects that are within the pool and then ultimately clear the pool itself”so now after the pool drain if you try to access any of the function of the object which were inside the pool, again your application will crash.

Step 5: But this does not change any rule of the retain count now lets say I make some changes in my code by adding just a small retain message to my object so this increase the retain count of my object by 1 and makes the final retain count to 2

#import <Foundation/Foundation.h>
#import "Employee.h"

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Employee *emp = [[Employee alloc]init];
[pool addObject:emp];//adding emp to pool...
[emp doSomething];
[emp retain]; //incrementing the retain count here
    [pool drain];
[emp doSomething];
    return 0;
}

Now if I access the function doSomething after the pool drain then in this case my application will not crash because as per the retain count algorithm my object still has one reference in the memory so this will work fine.





Note: In your application when you find that you no longer require an object and its work is completed then make sure that you pass a release message then and their to that object, if their are some objects who are globally declared then in this case you can use the dealloc function to release those objects.

I hope this post has helped you in clearing your concepts of memory management, your comments good or bad are always welcome as they help me in improving my writing skills.
Happy iCoding and have a great Day.

3 comments: