Wednesday, June 1, 2011

Memory Management in iOS

If you are an iOS Application Developer then your one of the major concerns would be to keep your memory allocations as low as possible. But sometimes is inevitable to keep memory low even if you adopt techniques such as Lazy Loading.
The cool feature in iOS is that it calls certain methods in your ViewControllers when it encounters Low Memory. The following methods are called when the iOS encounters a Low Memory and warns your ViewControllers about it.


- (void)didReceiveMemoryWarning

{

// Releases the view if it doesn't have a superview.

[super didReceiveMemoryWarning];

// Release any cached data, images, etc that aren't in use.

}

- (void)viewDidUnload

{

[super viewDidUnload];

// Release any retained subviews of the main view.

// e.g. self.myOutlet = nil;

}


Most developers know about that the above methods are called when Memory is Low, but do not know what exactly to do in these methods. Well In this post I am going to explain whatever doubts you might have regarding Properties and Nibs and how to handle them in these methods.

First of all we need to understand What are ObjectiveC Properties ?
This is how we specify properties in Objective C and most of the examples from Apple and other developers follow this,

So in your ViewController.h file you specify;


@property (nonatomic, retain) MyClass *obj;


and in your .m file


@synthesize obj;


and in your dealloc method add this line


[obj release];

you assign your ViewController to handle this in the following way:

MyClass *tempObj=[[MyClass alloc] init];//tempObj retain count=1

[self setObj:tempObj]; //tempObj retain count=2

[tempObj release]; //tempObj retain count=1

Assign the newly allocated "tempObj" to your property "obj" and release "tempObj" because it is retained by your property "obj".

After this you can also do this if you want to:

[self setObj:nil]; //tempObj retain count=0

This will release any previous instances retained by your property "obj" and assign it to "nil" hence not leaking any memory. This right here is the beauty of Properties. Even if you would have assigned it some new object, it would have first released the previously retained instance and then retained the new object.
So this means that we do not need to call release on our properties except for our dealloc method:

[obj release]; // only in dealloc methods.

[self setObj:nil]; // in other methods.


Now that we know what Properties can do. We just need to understand that how iOS wants you to react to Memory Warnings it generates in Low Memory conditions.
In your ViewController; iOS will do the following:

Call "didRecieveMemoryWarrning" method to warn your controller about low memory condition.
Here you can choose to do nothing if you have a simple enough implementation of your ViewController.

- (void)didReceiveMemoryWarning

{

// Releases the view if it doesn't have a superview.

[super didReceiveMemoryWarning];

// Release any cached data, images, etc. that aren't in use.

}



- (void)viewDidUnload

{

[super viewDidUnload];

// Release any retained subviews of the main view.

// e.g. self.myOutlet = nil;

}


Then iOS releases the NIB file loaded for that ViewController and calls "viewDidUnload" method. In this method you must assign your properties to nil.

So that your ViewController object is drained from the memory and has nothing but some placeholder iVars(properties) that have nil values. This will clear up the memory and your app can work fine after this.

That is all there is to know about memory management in Objective C and iOS.


Question: But this really wouldn't help my app to work fine or would it (I just lost all my data)? how can I re-assign my Properties? What will happen to my currently visible ViewController? Will I loose on screen data?


Answer: It did help your app to work fine actually. If you are in a Navigation Stack this really helped your application in running out of memory.

That means all your vViewControllers are in UINavigationController's stack then the following will happen:


UINavC > VC1 > VC2 > VC3


Suppose you are in VC3 and receive a memory warning, maybe because you are loading a big PDF document in there, then iOS will call the Memory Warning methods i.e "didRecieveMemoryWarning" & "viewDidUnload" in the whole Navigation Stack except in your visible ViewController VC3. Hence not loosing the currently being displayed information. Now as you go back(popViewController:VC3) to VC2, which has already gone under the process of releasing all its outlets and datasource perhaps, iOS will reload the VC2 Nib file and will call "viewDidLoad" method on VC2 giving it a chance to reassign its iVars all over again. That is why in most implementations "viewDidLoad" is thee method where you assign values to properties and get data sources etc. And since on popping VC3 it gets released by the UINavigationController the memory will reduce automatically.


- (void)viewDidLoad

{

[super viewDidLoad];

}


Then as you go and pop VC2, VC1 will get loaded again and so on and so forth.


I am not sure if this is the same case with UITabBarController at this point but you can give it a try.


It is pretty simple but you have to take care of this sequence when you are writing down your ViewControllers implementation. Not to put something that will get released once you receive a memory warning but will not get it back once the "ViewDidLoad" is called again on your ViewController.


So plan ahead of things.


I hope you gained something out of it.


Write your questions or suggestions below if you have any.


0 comments:

Post a Comment