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.


Saturday, March 5, 2011

UISplitViewController with details view controller on the left side.


The iPad SDK supports a new type of controller which is called UISplitViewController. This controller has the capability to display the two UIViewControllers (master, detail) so as to take full advantage of the iPad's bigger screen. A typical scenario is the one where you have a UITableViewController(left) and a DetailViewController(right) and the contents of the DetailViewController changes as the user taps on the different rows of the TableViewController. In portrait orientation only the DetailViewController is displayed while the TableViewController is typically displayed as a PopOverController.

While I was working on my SplitView based App that supports Arabic language, I needed the DetailViewController on the Left side instead of the Right in Landscape mode. This turned out to be even easier than I imagined it to be actually and so here is the code and explanation.

First we are going to subclass UISplitViewController

1. Add a new UIViewController to your project and name it "CustomSplitViewController"

2. Open up CustomSplitViewController.h and change it so that it looks something like this:

#import <UIKit/UIKit.h>


@interface CustomSplitViewController : UISplitViewController {


}


@end

-Here we changed the Parent Class from UIViewController to UISplitViewController

3. Save this and open up CustomSplitViewController.m and add the following method to it:

-(void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

{

[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];


NSMutableArray *controllers=[NSMutableArray arrayWithArray:self.viewControllers];

UIViewController * first=[[controllers objectAtIndex:0] retain];

UIViewController * second=[[controllers objectAtIndex:1] retain];


[self setViewControllers:[NSArray arrayWithObjects:second,first,nil]];

NSLog(@"%@\n%@",first,second);


[first release];

[second release];

}

-The UISplitViewController when initialized, takes an array of exactly two UIViewController classes and considers The Controller at Index zero as master and Detail Controller at index one. So here we swap the two as the orientation of the device changes. Simple and easy.

4. Once added, your CustomSplitViewController.m should something look like this:

#import "CustomSplitViewController.h"


@implementation CustomSplitViewController


- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration

{

[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];


NSMutableArray *controllers=[NSMutableArray arrayWithArray:self.viewControllers];

UIViewController * first=[[controllers objectAtIndex:0] retain];

UIViewController * second=[[controllers objectAtIndex:1] retain];


[self setViewControllers:[NSArray arrayWithObjects:second,first,nil]];

NSLog(@"%@\n%@",first,second);


[first release];

[second release];

}


// Override to allow orientations other than the default portrait orientation.

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {

// Return YES for supported orientations.

return [super shouldAutorotateToInterfaceOrientation:interfaceOrientation];

}


- (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;

}



- (void)dealloc {

[super dealloc];

}



@end

Now you can use your CustomSplitViewController any way you want to in your project.

Saturday, January 23, 2010

Break A Leg

I was hired a couple of months back as a Software Engineer and was asked to do some research about iPhone SDK etc to develop an app for iPhone. It has been 2 months now and I wouldn't hesitate in saying that "my research work is still in progress" but the good part is that almost 35-40% of development has also been carried out DURING THIS VERY PERIOD. Hell yeah! that is amazing, the development is so rapid and an "everyday coding success" gets you so much motivated (so much that you would want to start your own blog about it). This might seem a bit exaggerating but you would agree with me as soon as you get on the track.

So the big question "
HOW TO GET ON TO THE iPhone Dev TRACK? " has a real simple answer which is to get ready for reading a lot of lengthy material at Apple's development center (i.e developer.apple.com/iphone)where you would obviously get confused which article to read first because every article seems pre-requisite of each other. You might know of what I am talking about if you have already been there and read some of the material. The worst part of apple's material is that there is too less code and major, MAJOR concepts of Objective C and Cocoa. Well these are essential but where is the basic stuff and simple codes Apple?

Sorry I just got a little carried away and would want this blog to be of the same style as Apple's "documentation" so lets jump to the real stuff i.e how do I develop for iPhone. Below is a list of all the things that you would need and learn as you get started with the whole process:

  • Get Motivated that you are entering a very hot market
  • Get a Mac 10.5.7 or Above (or a PC with Hackintosh would suffice , yes yes it will do the job don't worry)
  • Create a FREE apple's developers account (developer.apple.com/iphone)
  • From there download the latest iPhone SDK (with XCode,Interface Builder and iPhone simulator)
  • Install the SDK
  • Open XCode and start playing.
I will give an explanation to each of these steps in my later posts. Hope this was helpful.