Sunday, April 24, 2011

Memory Management

An important concept in programming is memory handling. If you come from C you might know of pointers, they point to a certain memory location. So if you declare
int *num
The num variable will point to a certain location in memory. To reserve this memory for future use in C malloc or calloc is used, and after this space is reserved it is totally our responsibility to handle it, the system won't use this space at all during the execution of the program. So let us reserve 10 bytes of memory by using malloc and letting num point to that block of memory. Now in case you have done all of this in a function, the memory is reserved though out runtime but the num pointer won't be accessible outside the function. So oustide the function even though the memory is reserved you won't be able to access that memory and make changes to it.


Memory management in iOS is also very similar to this, almost all variables in iOS are pointers and memory is allocated to it through the alloc function. For ex if you want to allocate memory to an NSString pointer variable, you would do something like
NSString *string = [[NSString alloc] init];


So now the string has been allocated memory and you may use it in your code. Now since you are the one who allocated memory to the string you should be the one to deallocate memory from it. Now the SDK offers a dealloc method which you can call explicitly to free up the allocated memory but this isn't considered good practice, instead the SDK provides a convenient way to tell the OS that you have done your bit towards memory handling and now it is totally the system's responsibility to take care of the memory and ensure that it doesn't leak. This practice comes in handy when the object is needed by the system but you don't need a reference to it anymore, for ex after adding a viewcontroller to the navigation controller, a reference to the view controller isn't generally required. So you would tell the OS that I am releasing the object from my side and now it is your responsibility to take care of it. This code fragment will better explain the view controller and navigation controller example
UIViewController *vc = [[UIViewController alloc] init];
UINanigationController *navController = [[UINavigationController alloc] initWithRootViewController:vc];
[vc release];


So in the first line we allocate memory to the view controller, in the second line we allocate memory to a navigation controller and assign the previously declared view controller as the navigation controllers root view controller. After doing this we no longer need a reference to vc so we call the release method on vc and tell the os to take care of the memory,


So whenever you call alloc be sure to call release otherwise you will create a memory leak and increase the memory footprint of your app, you sure don't want that to happen.

Most common ios design patterns

When you have limited screen real estate design becomes very important. Putting in a lot of data without making the screen cluttered in very important when designing applications. This takes a lot of thinking and fortunately the engineers and ui experts at apple have done this thinking for you. They have provided easy to use design patterns in their sdk for making good, presentable and usable apps.

Some of these deign patterns are

  • Tab Bar
  • navigation bar
  • table views
  • toolbars
Tab Bar
This design pattern presents a bar at the bottom of the screen with 3-5 icons on it which present different views. 

Navigation bar

This patterns presents a hierarchy of views, which are related providing mechanisms to move back and forth in the heirarchy.

Table View
This view presents a scrollable list of items with each item very often presenting a view of its own. It is extremely effective in presenting a lot of data in limited screen size. 

Tool bar
This was introduced in iOS 3.0 (it was called iPhone OS back then), its position is same as the tab bar, i.e at the bottom of the screen. You can see it in safari, mail and a lot of other applicaitons

Personal opinion, I believe putting a toolbar on top of a tab bar is a horrible idea.

For further reading you can refer to apple's iOS Human Interface Guidelines ,
also pttrns is a great site which assembles the best UIs in the AppStore for your reference http://pttrns.com/ 


plists

If you have ever gone through the ios filesystem you will find lots and lots of plists. Plists or property lists are basically xml files used to store information on the iphone.

A plist can store arrays, strings, nsdata, numbers, dates and nsdictionaries.



A plist has a basic structure, with one root element and everything under it.

Let us make a plist to save contacts (this may be a bad practice and is only for illustration purposes)
So to start with let the root element of our plist be an array which will hold all the contacts.
Each element of the array will be an nsdictionary with key value pairs as follows

  • id - nsnumber
  • name - nsstring
  • phone - nsstring
It is a very simple structure and the iOS sdk offers a lot of convenience methods to load data from plists into arrays or dictionaries.

So during the first run of our program we would want to create a plist and save it to some locations, this location will most likely be your documents directory since it is automatically backed up by iTunes.

So first we need to get the path for the documents directory of our app, we get this through the following code 

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *writeReadPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"contacts.plist"];

As you see the second line has the filename of the plist appended to the path of the documents directory we got, in this case we have named the file to be contacts.plist .

Now that we have got the path to our file, let us proceed to writing data on our file. If it is the first run of the app there is a possibility the file doesn't exist, to tackle this issue we add a few lines of code and create the plist with sample data

NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:writeReadPath]) {
     NSDictionary *contact1 = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects:[NSNumber numberWithInt:1],@"John",@"1944232321",nil] forKeys:[NSArray arrayWithObjects:@"id",@"name",@"phone",nil]];
 NSDictionary *contact2 = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects:[NSNumber numberWithInt:2],@"Steve",@"1944232421",nil] forKeys:[NSArray arrayWithObjects:@"id",@"name",@"phone",nil]];
 NSDictionary *contact3 = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects:[NSNumber numberWithInt:3],@"Peter",@"1944132321",nil] forKeys:[NSArray arrayWithObjects:@"id",@"name",@"phone",nil]];
     NSArray *contacts = [NSArray arrayWithObjects:contact1,contact2,contact3,nil];
[contacts writeToFile:writeReadPath atomically:YES];
}

With this much of code we have managed to create a new plist file, add contacts to it, without even going to deep into file handling. Thanks to the convenience methods of nsarray and nsdictionary it becomes very easy to write data into plists. In future posts we will deal with editing and deleting items from a plist.

UITabBarController

This is another common design pattern found in iOS apps, it includes a bar (generally black) at the bottom of the screen with various icons which when tapped load up different views.

tabbar

Each icon (button) is actually a separate viewController with the title of the viewController displayed below each of the icon.

The uitabbarcontroller has a viewControllers property to which an array of viewControllers is assigned. Generally tabbarcontrollers have four or five viewControllers. In case there are more than 5 view controllers a more icon is created like the one seen above which has the rest of the viewcontrollers.

Saturday, April 23, 2011

UITableView

The UITableView is a very common design pattern used in iOS apps. They help in presenting a lot of data in limited screen real estate.



Setting up a UITableView requires a delegate and a data source to be implemented. In case you're not familiar with the delegation and data source concepts, think of it like this, the delegate is a class which tells the OS, how to present the table view, for ex its size, the modes in which the table view should be like editing, normal etc. The data source, as the name suggests provides data to the table view to present it on the screen.

This was about the concept of delegation and data source, to actually implement it, you should declare one of your classes (view controller, preferably with a nib file as well) as the UITableViewDelegate and the UITableViewDataSource. This is done by adding <UITableViewDelegate>, <UITableViewDataSource> after the class declaration in the header (.h) file, like this :

@interface MainViewMulti : UIViewController <UITableViewDelegate,UITableViewDataSource> {
//class members
}

To actually add the table view you can either do it through code or interface builder. Interface builder is preferred if you have relatively simple tasks. So click on the nib file linked with your view controller, it will open interface builder. Now in the library search for tableview and drag it to your nib.



After doing that right click on the table view you just dragged, there will be delegate and datasource outlets, drag it to the files owner. This tells the table view that the class you've declared is its delegate and data source.


Now that you have declared your class as the delegate and the data source it is time to implement mandatory methods which will provide data and set up the the tableviewcells.

The apple documentation says that the following methods are mandatory


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

The first method tells the device, how many rows do you want, that value is specified by the return value of that method.

The second method tells the device what contents to put in each row of the table view. This method is called for each row.

Right now you have the basic framework required for setting up a table view. You can use this as a template for all of you tableviews.

Now to actually put data in your table view let us declare an array in the header file, and populate it with nsstrings. Let us name the array tableDataSource, to populate it insert the following code in your viewDidLoad method


tableDataSource = [[NSArray alloc] initWithObjects:@"Monday", @"Tuesday", @"Wednesday", @"Thursday", @"Friday", @"Saturday", @"Sunday",nil];

In case you aren't familiar with arrays, this is one of the convenience methods to directly assign an array with objects.

Now the table view will contain as many rows as the number of objects in the array, so to tell this to the os add the following line to your -  numberOfRowsInSection method
return tableDataSource.count

The only thing left now is to populate each row of the tableview with the items in the array, to do this add the following code to your cellForRowAtIndexPath method


static NSString *SimpleTableIdentifier = @"SimpleTableIdentifier";
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:SimpleTableIdentifier] autorelease];
}

NSUInteger row = [indexPath row];
cell.textLabel.text = [tableDataSource objectAtIndex:row];
return cell;

Now save everything (including the nib files) build your project and you should see the table view with the items you set in the simulator.

Next we will discuss how to put the table view in edit mode and how to add and delete items from the tableview.