Sunday, January 1, 2012

Automating Recovery after Failure

A Simple Download Manager
Introduction
This report is aimed at discussing a design for an iOS application, which downloads files from the internet. Based on this design, the application is able to recover itself from failures, which disrupt the download process and make it stop. The discussion in this report covers the areas related to how the download states are saved in case of a failure, and how the application uses the saved information to resume previously in progress downloads. The example application uses ASIHTTPRequest library to perform network activities (in this case, simply downloading files), and uses ASINetworkQueue to manage multiple downloads. (A link to the source of the project discussed in this report is provided at the end of the report)

Application general description
The example download manager application uses two buttons to initiate downloads for a 5MB and a 10MB files. To enable an ASIHTTPRequest object to be able to resume not finished downloads, temporary download path can be set for partial downloads. Figure 1, shows the application in 3 different states.

Figure 1: The download manager application in 3 states: No downloads in queue, 1 download in progress and 2 download in progress

Note that in figure 1, when downloads are in progress, network activity indicator is animating on the status bar. Figure 2 shows the partially downloaded files on the hard drive. Note that the .download extension indicates that downloads are not completed.

Figure 2: partially downloaded files (download is still in progress)

To provide the ability to recover in the download manager application, information of download requests is kept in an array of dictionaries, called dlTracker (NSMutableArray* dlTracker). Every request has a unique identifier so that it would be recognizable in the entire application. For this purpose, the tag property of ASIHTTPRequest objects is used. Download buttons code snippets are provided below. (A link to the source of the project is provided at the end of the report)

- (IBAction)startDownloadFiveMBBtnPressed:(id)sender
{
    if (file5MBDownloadInProgress)
        return;
    NSURL *url = [NSURL URLWithString:@"http://download.thinkbroadband.com/5MB.zip"];
                 
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];

    NSString *downloadPath = [path stringByAppendingString:@"/5MB.zip"];   
    [request setDownloadDestinationPath:downloadPath];
   
    [request setTemporaryFileDownloadPath:[path stringByAppendingString:@"/5MB.zip.download"]];
    [request setAllowResumeForFileDownloads:YES];
   
    [request setDelegate:self];
    [request setDownloadProgressDelegate:progressViewFive];
    [request setShowAccurateProgress:YES];
   
    request.tag = 5;
    //[request startAsynchronous];
   
    [self takeRequestInfo:request];
    [queue addOperation:request];
    [queue go];
}

- (IBAction)startDownloadTenMBBtnPressed:(id)sender
{
    if (file10MBDownloadInProgress)
        return;
    NSURL *url = [NSURL URLWithString:@"http://download.thinkbroadband.com/10MB.zip"];   
   
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
   
    NSString *downloadPath = [path stringByAppendingString:@"/10MB.zip"];   
    [request setDownloadDestinationPath:downloadPath];
   
    [request setTemporaryFileDownloadPath:[path stringByAppendingString:@"/10MB.zip.download"]];
    [request setAllowResumeForFileDownloads:YES];
   
    [request setDelegate:self];
    [request setDownloadProgressDelegate:progressViewTen];
    [request setShowAccurateProgress:YES];
   
    request.tag = 10;
    //[request startAsynchronous];
   
    [self takeRequestInfo:request];
    [queue addOperation:request];
    [queue go];
}
Code snippet 1: IBAction methods for initiating downloads
The application going to the background, network getting disconnected, and force stop by user; will be discussed as failure scenarios in the following. It should be mentioned that ASIHTTPRequest does not stop its file transferring process in the background. However, in this design, downloads are forced to stop in background, to better show the functionality of the application. A better design would be downloads remain in progress in background and be saved and stopped when the application is about to terminate.


First failure scenario: Application going to the background
When the application is about to enter background, downloads are stopped and the data being kept in dlTracker array is written to a file called “dlTracker.if”. Information kept for each download request includes, URL, download path, download temporary path and request unique identifier. Figures 3, shows that the dlTracker.if file comes into existence when the application enters background, and figure 4, shows the dlTracker file format.

Figure 3: dlTracker.if file is created as the application enters background

Figure 4: dlTracker file format

It should be mentioned that dlTracker.if file is only created when there are download requests in download queue. Therefore, when re-entering foreground, the application uses this condition, which is the existence of dlTracker.if file, to determine if there were any download requests in progress when the application entered background. So, on changing state from background to foreground, if dlTracker.if file exists, user is asked for confirmation on resuming downloads. If user chooses to continue downloads, the requests are recreated based on their information in dlTracker.if file, and are resumed from the point indicated by partially downloaded files. As downloads begin to continue, dlTracker.if file is also removed.

Figure 5: dlTracker.if file is erased if user decides to continue.

If any of downloads finishes, its data will be removed from dlTracker array. Therefore, in case of any interruption, the finished process information will not be written to file. This is how not attempting to resume completed processes by the application is handled.

Figure 6: one of the download requests has been completed

Figure 7: dlTracker.if file does not contain any information about the finished process

Code snippets for parts discussed in this section are provided below. (A link to the source of the project is provided at the end of the report)

- (void)takeRequestInfo:(ASIHTTPRequest*)request
{
    NSString* requestTag = [[NSString alloc] initWithFormat:@"%d",request.tag];
    NSString* url = [[NSString alloc] initWithString:[[request url] description]] ;
   
    NSArray* objects = [[NSArray alloc] initWithObjects:
                        requestTag,url,
                        [request downloadDestinationPath],
                        [request temporaryFileDownloadPath],nil];

    NSArray* keys = [[NSArray alloc] initWithObjects:
                        @"tag",@"url",
                        @"downloadPath",
                        @"tmpDownloadPath",nil];
   
    [requestTag release];                       
    [url release];
                       
    NSDictionary* requestInfo = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
   
    [objects release];
    [keys release];
   
    [dlTracker addObject:requestInfo];
    [requestInfo release];
}
Code snippet 2: Tracking requests in an NSMutableArray.

- (void)willResignActive:(NSNotification*)notification
{
    [self performSelector:@selector(saveRequestInfo)];
}

- (void)saveRequestInfo
{
    if ([dlTracker count]>0)
    {
        if (queue != nil)
            [queue cancelAllOperations];
        [dlTracker writeToFile:[path stringByAppendingString:@"/dlTracker.if"] atomically:YES];
        [dlTracker removeAllObjects];   
    }
}

- (void)willEnterForeground:(NSNotification*)notification
{
    [self performSelector:@selector(initializeNetworkOperation)];
}
Code snippet 3: Application changing state between background and foreground.

- (void)initializeNetworkOperation
{
    if (reachability == nil)
        reachability = [[Reachability reachabilityForInternetConnection] retain];
    [reachability stopNotifier];
    [reachability startNotifier];

    if ([reachability currentReachabilityStatus] == NotReachable)
    {
        [self performSelector:@selector(updateNetworkStatus:)
                              withObject:@"No Internet availability"];
        return;
    }
    [self performSelector:@selector(updateNetworkStatus:)
                              withObject:@"Internet available"];
   
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [[NSNotificationCenter defaultCenter] addObserver:self       
                             selector:@selector(reachabilityChanged:)
                             name:kReachabilityChangedNotification object:nil];   
   
    if (queue == nil)
    {
        queue = [[ASINetworkQueue alloc] init];
        [queue setDelegate:self];
        [queue setDownloadProgressDelegate:progressViewQ];
        [queue setShowAccurateProgress:YES];
        [queue setQueueDidFinishSelector:@selector(queueComplete:)];   
        [queue setRequestDidStartSelector:@selector(queueInProgress:)];
    }
   
    [[NSNotificationCenter defaultCenter] addObserver:self
                    selector:@selector(willEnterForeground:)                                                 
                    name:@"UIApplicationWillEnterForegroundNotification" 
                    object:nil];   
    [[NSNotificationCenter defaultCenter] addObserver:self
                    selector:@selector(willResignActive:)
                    name:@"UIApplicationWillResignActiveNotification"
                    object:nil];
   
    if (dlTracker == nil)
        dlTracker = [[NSMutableArray alloc] init];
   
    if ([self performSelector:@selector(checkForIncompleteRequests)])
    {
        UIAlertView* alertView = [[UIAlertView alloc]
                                   initWithTitle:@"Incomplete Downloads"
                                   message:@"Continue incomplete downloads?"
                                   delegate:self
                                   cancelButtonTitle:@"No"
                                   otherButtonTitles:@"Yes",nil];
        [alertView show];
        [alertView release];
    }   
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
        [self performSelector:@selector(recoverRequests)];
}

- (void)recoverRequests
{
    NSMutableArray* mArray = [[NSMutableArray alloc] initWithContentsOfFile:[path stringByAppendingString:@"/dlTracker.if"]];
   
    NSFileManager* fileManager = [NSFileManager defaultManager];
    [fileManager removeItemAtPath:[path stringByAppendingString:@"/dlTracker.if"] error:nil];
   
    for (NSDictionary* requestInfo in mArray)
    {
        NSURL *url = [[NSURL alloc] initWithString:[requestInfo objectForKey:@"url"]];
        ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:url];
        [url release];
       
        [request setDownloadDestinationPath:[requestInfo objectForKey:@"downloadPath"]];
        [request setTemporaryFileDownloadPath:[requestInfo objectForKey:@"tmpDownloadPath"]];
       
        NSNumber* requestTag = [requestInfo objectForKey:@"tag"];
        request.tag = [requestTag intValue];
       
        [request setDelegate:self];
        [request setAllowResumeForFileDownloads:YES];
       
        if (request.tag==5)
            [request setDownloadProgressDelegate:progressViewFive];
        else if (request.tag == 10)
            [request setDownloadProgressDelegate:progressViewTen];
        
        [request setShowAccurateProgress:YES];
       
        [self takeRequestInfo:request];
        [queue addOperation:request];
        [request release];
    }
   
    [mArray release];
   
    if ( [queue operationCount]>0 )
        [queue go];
}
Code snippet 4: Network operation initialization including checking for incomplete downloads and initiating downloads recovery.

Second failure scenario: Internet is not available
When the application is initialized, an object of type Reachability is created, which is constantly observing Internet connection. By every change in network status, Reachability object notifies the application of the change. This is where network failures are actually handled. As soon as the application is notified by a change in network status, and if it determines that the internet is not available anymore, dlTracker.if file is generated; containing the data related to active requests. When internet connection is back, saved requests are recovered through the same process as discussed before. Figure 8, illustrates the screenshots when internet is disconnected and connected again. Note that internet availability is shown on the application’s screen, and as mentioned before network activity indicator animates when requests are in progress. Code snippets for parts discussed in this section are provided below. (A link to the source of the project is provided at the end of the report)

- (void)reachabilityChanged: (NSNotification* )note
{
    Reachability* curReach = [note object];
    NSParameterAssert([curReach isKindOfClass: [Reachability class]]);
    if ([curReach currentReachabilityStatus]==NotReachable)
        [self performSelector:@selector(internetDisconnected)];
    else
        [self performSelector:@selector(internetConnected)];
}

- (void)internetDisconnected
{
    NSLog(@"Damn it network's gone");
    [self performSelector:@selector(updateNetworkStatus:) withObject:@"No Internet availability"];
    [self saveRequestInfo];
}

- (void)internetConnected
{
    NSLog(@"Phew, network's back");
    [self performSelector:@selector(updateNetworkStatus:) withObject:@"Internet availabe"];
    [self initializeNetworkOperation];
}
Code snippet 5: Reachability object delegates

Figure 8: Internet connection availability

Third failure scenario: force stop by user
Two buttons, stop and resume, are provided for users, so that they can manage their download requests manually. The functionality of these buttons and what that happens in their IBAction methods is exactly the same as previous scenarios; except for the fact that unlike previous scenarios, when touching resume button, user is not asked for confirmation. This is in accordance with iOS Human Interface Guidelines 2011, which says it is not considered a good practice to take advantage of alert views to take users’ confirmation for tasks they have initiated themselves.

Conclusion
This download manager application is able to recover itself from almost any type of interruption. However, it may not be a good practice to keep track of requests similar to the way used here. Specially relying on existence of a file to determine if there are any downloads to resume, can be quite risky. Finally, as mentioned before, it would be better if downloads remained in progress in background and were saved and stopped when the application was about to terminate.
References

Saturday, December 31, 2011

MVC Pattern

Separate Your Concerns II
This report is aimed at discussing a table view application that sources data from the Internet. The application should have the following features :
  • The application should separate data model, control logic, and display code following the MVC pattern. It is acceptable to control views from your controllers (e.g. set the background color of the view); it is not acceptable to implement how the views are drawn (e.g. implementing drawing commands that actually color the background).
  • The user should be able to refresh, which makes a call to the Internet, updates the application’s data, and then updates the table view.
  • Application must be able to work offline.
Good object-oriented design will help code be resilient to changes, new features, and portability. Apple uses the Model-View-Controller design pattern as part of the UIKit framework on which most iOS apps are built. In the following, how Model-View-Controller design pattern is used to implement class structure and communication between classes is explained. In addition, how this design will resiliently handle possible changes is also explained.

The application
The following screenshots illustrates an application which gets the current foreign exchange rates for approximately 66 currencies from Time Genie website (URL is provided in references section). The base rate is the Euro and so the Euro's value will always be 1. The application saves the data in a comma-separated format file and presents it in a UITableView to users. The file containing the data can be synchronized with its source (Time Genie website) using the Refresh Data button provided (Data is updated on daily basis by the Time Genie website).

A link to the source code of the application is provided at the end of the post.

Image 1: Application in 3 states (from left to right, before refreshing, refreshing and after refreshing)

Note that in the image above, before refreshing the data, the first 7 items in the file was manipulated manually to show the effect of the refresh button.

MVC pattern in exchange rates viewer application
The exchange rates viewer application presents currency rates for approximately 66 countries, based on Euro, in a table view format. The data is sourced from a website in comma-separated text format. The application saves a copy of the file the first time it connects to the source website; thereafter, the data is loaded from the file. This functionality and all other relevant operations are handled in the model layer of the application, which is a class called DataProcurer and is a subclass of NSObject.

Model layer checks the existence of the data file in its init method using an NSFileManager. In case the file does not exist, information is read from the source website and is saved to a file in application directory so that it can be used by the application for the next times. It provides the model with the ability to produce data even if there is no connection to the internet. Model layer has an instance variable of type NSMutableArray called currencyRates using which information is communicated to other layers. Instance variable currencyRates can be accessed from outside of the model layer via its accessor methods (accessor methods are generated using @synthesize notation).

There are other methods in model layer that are used to manipulate data. Method refreshData simply deletes the data file using a NSFileManager and tries to repopulate the instance variable currencyRates using the same method that is called in init method. Therefore, because the file does not exist anymore, the updated version will be provided from the source website. The source code for important methods of the DataProcurer class is provided in the following.

-(id)init
{
    self = [super init];
    if (self != nil)
    {  
        currencyRates = [[NSMutableArray alloc] init];
        currencyRates = [self giveMeData];
    }

    return self;
}

-(void) refreshData
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(
                                         NSDocumentDirectory,
                                         NSUserDomainMask, YES);
    NSString *docDirPath = [paths objectAtIndex:0];
    NSString *filePath = [docDirPath
                  stringByAppendingPathComponent:@"mydata.txt"];
      
    NSFileManager* fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:filePath])
    {
        [fileManager removeItemAtPath:filePath error:nil];      
    }   
    currencyRates = [self giveMeData];
}

- (NSMutableArray*)giveMeData
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(
                                       NSDocumentDirectory,
                                       NSUserDomainMask, YES);
    NSString *docDirPath = [paths objectAtIndex:0];
    NSString *filePath = [docDirPath
                     stringByAppendingPathComponent:@"mydata.txt"];
    NSURL* url;
    NSString* content;   
    NSFileManager* fileManager = [NSFileManager defaultManager];
   
    if ([fileManager fileExistsAtPath:filePath])
    {
        content = [[NSString alloc]
                   initWithContentsOfFile:filePath
                   encoding:NSUTF8StringEncoding error:nil];      
    }
    else
    {
        url = [[NSURL alloc]
              initWithString:@"http://rss.timegenie.com/forex.txt"];
        content = [[NSString alloc]
                   initWithContentsOfURL:url
                   encoding:NSUTF8StringEncoding error:nil];
        [url release];
        [content writeToFile:filePath atomically:YES
                    encoding:NSUTF8StringEncoding error:nil];
    }
   
    NSMutableArray* tmpMutableArray = [[NSMutableArray alloc]
                               initWithArray:[content
                               componentsSeparatedByString:@"\n"]];
    [content release];
   
    NSMutableString* currency = [[NSMutableString alloc] init];
    NSMutableString* rate = [[NSMutableString alloc] init];
    NSMutableArray* mutableArray = [[NSMutableArray alloc] init];
    for (int i=0; i < [tmpMutableArray count]; i++)
    {
        NSMutableString* tmp = [[NSMutableString alloc] init];
        [tmp setString:[tmpMutableArray objectAtIndex:i] ];
       
        int l = [tmp length];
        if (l > 0)
        {
            [currency setString: [[tmp
                                  componentsSeparatedByString:@"|"]
                                 objectAtIndex:0] ];
            [rate setString: [[tmp
                               componentsSeparatedByString:@"|"]
                             objectAtIndex:2] ];
       
            [tmp setString:@"Every 1 EURO is "];
            [tmp appendString:rate];
            [tmp appendString:@" "];
            [tmp appendString:currency];
       
            [mutableArray addObject:tmp];
        }
       
        [tmp release];
    }
   
    [tmpMutableArray release];
    [currency release];
    [rate release];
   
    NSMutableArray* r = [[NSMutableArray alloc]
                         initWithArray:mutableArray];
    [mutableArray release];
   
    return r;
}

The controller layer, Assignment6ViewController class which is a subclass of UIViewController, mediates between model and view layers. To do so, it has an instance variable of type DataProcurer, the model layer, and an IBOutlet of type UITableView that is associated with the table view in the view layer. In the viewDidLoad method of the controller class, the instance variable of type DataProcurer is set to act as the datasource of the table view. It should be mentioned that DataProcurer class adopts the UITableViewDataSource protocol.

Touching Refresh Data button triggers an IBAction method in the controller class. In this method all that has to be done is to send a refreshData message to the model layer so that information can be sourced from the internet again. Consequently, the table data will need to be reloaded which is done after calling refreshData in the IBAction method. The source code for important parts of the Assignment6ViewController class is provided in the following:

- (void)viewDidLoad
{
    [super viewDidLoad];
    dataProcurer = [[DataProcurer alloc] init];
    tableView.dataSource = dataProcurer;
}

-(IBAction) btnRefreshDataPressed:(id)sender
{
    [self.view addSubview:modalView];
   
    [self performSelectorInBackground:@selector(background)
                                           withObject:nil];
}

-(void)background
{
    NSAutoreleasePool *p=[[NSAutoreleasePool alloc] init];
    [dataProcurer refreshData];  
    [tableView reloadData];
   
    [self performSelectorOnMainThread:@selector(backgroundDone)
                                     withObject:nil
                                      waitUntilDone:NO];
    [p release];
}

-(void)backgroundDone
{
    [self.modalView removeFromSuperview];
}
@end

The view layer, generated by Interface Builder, uses a table view to show the data. As discussed in previous sections, the view layer gets its required data from the model layer via an intermediary object that is the view controller. Using Interface Builder, no code is written and everything is exclusively focused on the View of the application (Cocoa 2011).

There are some points at which exchange rates viewer application could be improved. The application is currently storing its data in a flat file in a comma-separated format. It would be better if information was stored in NSManagedObjects using core data. In that case, firstly, information could be sourced incrementally if needed. Whereas now, the data file is deleted and regenerated every time an update is necessary. Secondly, the data model could be expanded to keep the date of the information so that it could be checked whether or not the data needs to be updated when the application runs. This check could happen automatically in model layer or instead, model layer could provide a method for controllers so that they can ask the model to do so.

Switching the model layer from using flat files to core data will be simple in this application. All the changes will be limited to the model layer and the way in which the data is stored. Communication of information between model and view controller will remain unchanged. The view controller does not know and does not need to know how information is stored. All it needs is an array of strings to use as the data source of the table view IBOutlet that it is keeping; no matter how the array is generated or where it is sourced from.

Diagram




References

iOS App Life Cycle

iOS applications don’t always live in the foreground. Applications can be foreground, background, suspended, and active or inactive, depending on the circumstances. UIApplication object uses different methods in its delegate to provide information about when important changes in application’s state occur or is about to occur. Implementing delegate methods helps the application avoid crashing and gives it the ability to continue where it stopped.

Imagine that you are asked to develop a first-person shooter 3D game for iOS in OpenGL (an open standard for 3D graphics written in C). The game has levels, multiplayer capabilities, and a global leaderboard (connected through the Internet). The aim here is to create a diagram that explains what work your game will do when transitioning between each of the application’s states. The diagram must cover at least the following possibilities:
  • User receives a text message in the middle of a single player game (iOS4).
  • User receives a phone call in the middle of a multiplayer game and decides to answer the call.
  • User closes the app with the home button in the middle of a single player game.
  • User presses the lock button while on the Main Menu of the application, outside of a game.
Attention should be paid to iOS requirements (e.g. iOS apps that use OpenGL in the background will be terminated by iOS!).

In the first-person shooter 3D game for iOS example, when playing a single player, after an interruption like receiving an SMS or user pressing home button, user data including which level, points and generally current circumstances should be saved. If Internet connection is in use for global leaderboard, it should be cancelled otherwise the connection object attempts to call its delegate, which does not exist anymore, when it is finished. For the same reason, any timers in use should be invalidated and finally OpenGL frames must stop working as the program is about to enter background as any OpenGL operation will cause the application to be closed by iOS.

Similar scenario is true when a multiplayer game is in progress and an interruption such as a phone call happens. Except that in a multiplayer game there is less or no user data to be saved and of course internet connection may not be an issue. However, the connectivity among the device and other pairs should be cancelled.

When a game is not in progress and the application is in the menu and an interruption occurs, there is still OpenGL operation which needs to be stopped. Furthermore, the application may already have an internet connection in use, which should be terminated, for instance, in case user have checked the global leaderboard.

The application should be able to recover its last state if necessary. This can happen when for instance user ends a phone call or decides not to open an SMS. In that case, user wants to be able to continue the game where it was paused. This reversal operation could take place in delegate methods that are indicating the application is about to go back to the foreground.

In the end, it is a plus for an application to handle these situations properly. However, some of the processes discussed above, both in moving to background and recovering from a halt, take a lot of time to be completed which can be frustrating for users if they happen many times. For example, 3D graphical objects consume a lot of resources to be created and eliminated. Moreover, not all the conditions are recoverable. For example when playing a multiplayer game, previous state is not achievable and user must re-join the game and start from the beginning.

Diagram


Tuesday, December 27, 2011

Delegates, Data Sources and Table Views

Separate Your Concerns
Multiple Data Sources!
A UITableView object requires two other objects as its data source and delegate to perform. Data source object, which should conform to UITableViewDataSource protocol, provides data for populating and information about data structure for table view objects. For this matter, there are two methods in UITableViewDataSource protocol, cellForRowAtIndexPath and numberOfRowsInSection, that data source objects are required to implement. Hence, it is compulsory for every table-view object to have a data source object. However a table-view object might not have a delegate as all the methods in UITableViewDataDelegate protocol are optional (Table View Programming Guide for iOS 2011).

In addition to data and its structure, configuration of table view objects is also determined by data sources. For example, whether or not table rows can be edited or deleted and if table rows can be reordered (UITableViewDataSource Protocol Reference 2010). A table-view object may need to act or be presented differently under certain circumstances. The argument here is having multiple data source objects which table-view object can switch to in different situations instead of having multiple table-view objects, or one table-view instance having a single data source with complicated controlling structures for various situations.

One situation when a single table view object with multiple data sources can come in handy is showing different data in similar formats. Imagine a twitter app in which tweets are categorized into 3 groups: re-tweets, mentions and home. Every type of tweet can be loaded into one data source and the table view switches among data sources when user wants to. It certainly does not make much sense implementing 3 table views. The reason for this is the fact that, every change, major or minor, in the way tweets are shown, should be applied on 3 table view objects. A multiple data sources design obviously needs less effort in this case. The diagram below shows how this design works in MVC.


Depending on the number of rows, a table-view object might have to present its data in different number of sections. In an application where dataset is to be provided from dynamic external sources like the web, the number of to be populated rows is not predefined. Therefore, the application should be ready to structure its table-view, during the runtime of the application, in ways suitable for the dataset it is encountering. It is simply achievable by having multiple data source objects prepared for different purposes, and defining the appropriate table-view data source as current data source when necessary. This design is also helpful in applications where users have the facility to change the appearance of their application, here particularly table-view objects, regardless of the amount of the data and the source from which the data is provided. In addition, situations when for instance the device rotates can be handled easily and effectively using the same approach. It can be a useful feature that an application represents its data in a table-view object differently depending on the device orientation.

Apart from the appearance of the data, the way in which table-view objects’ data should be treated in different situations is manageable via data sources. Following the design discussed above, there can be multiple data sources to allow users to edit, delete or to move rows of data in certain times; or prevent users from manipulating the data in some other times. This can be for reasons such as the origin of data or the stage at which the application is. For example, an application might provide its users with two states; one with the facility to change the order of the rows of a table-view object and the other where the content of the table-view object is frozen and cannot be changed.

Having a single UITableView object with multiple data sources in the scenarios discussed above, will lead to a smaller application that requires less memory in comparison to a design in which there are more than one UITableView objects, each of which being controlled by a separate data source. Moreover, it leads to having several objects as data sources, each of which simply responsible in one occasion. This is a lot clearer and easier to maintain than when all situations are handled in one data source that results in complicated controlling structure and an unreadable code.

References

Objective-C Memory management

Introduction
When creating applications for Mac OS X, it is possible to have garbage collection enabled which means you do not need to worry about freeing memory allocated to objects. However, in iOS, due to lack of resources including CPU and memory, you cannot take advantage of a garbage collection mechanism; and  therefore, allocating and releasing objects’ memory should be handled manually.
Memory management in Objective-C is based on object ownership, where an object can have several owners; and it will exist until it loses all of its owners. If an object dose not have any owners anymore, it will be destroyed automatically by the runtime system (Memory Management Programming Guide 2011). This model is referred to as reference-counting in which all that needs to be taken care of is: keeping track of references; and when there are no references to an object, it is the runtime system responsibility to free the memory allocated to the object.
The reference-counting model works based on the following rules:
  • You are the owner of the object you create. An object is created by alloc, new, copy and mutableCopy methods. When an object is created for the first time, the reference-count for it is 1.
  • You can take ownership of an object by sending it a “retain” message. A retain message increases the reference-count by 1. You take ownership of an object to make sure you do not lose it before you are finished with it.
  • You have to release an object after you are finished with it. This is done by sending the object a release or autorelease message. Every time an object receives a release message, its reference-count decreases by 1. Figure one shows how reference-counting works.
  • You should not attempt to release an object unless you have already created or retained it.
Reference counting
Figure 1: reference-counting and object validity in Objective-C (Stevenson 2008)
Retain
One occasion that an object is sent a retain message is when the object’s value is to be stored in a property of a class. This normally happens in “setter” and “initialization” methods to prevent the property from losing its value after the referring object (the input parameter of the setter or init method) is released by its owners. Another situation to use retain is wherever an existing object’s value is going to be assigned to a secondary object; and that secondary object should exist longer than the primary object. For instance, in situations when the primary object or its parent, is going to be destroyed in other parts of the program. Finally, when assigning an object returned by a method to another object, the returning object is retained (if necessary) because its reference count may become zero after the method finished executing. 

Release
Objects should be released as soon as they are no longer needed. To do so, a release or autorelease message is sent to the object. The difference of the two messages lies in the fact that the ownership of an object, sent a release message, is relinquished immediately and in case of having no more owners, the object is destroyed and the allocated memory is freed. Whereas in autorelease, it is guaranteed that the object will exist at least until the end of the current block. Therefore, autorelease seems to be safer than release. Nevertheless, in applications that have too many objects loaded into memory, defining all the objects as autorelease can cause memory inefficiencies. There are two specific occasions that autorelease is preferred to release. First, in setters, the property is sent and autorelease before it gets the new value provided by the input parameter. Reasoning behind this is the possibility that both the property and the input parameter could point to the same location (Stevenson 2008).
- (void) setProperty: (NSObject*) inputParameter
{
    [property autorelease];
    property = [inputParameter retain];
}
The second occasion to use autorelease is when the object is supposed to be returned by the current method (Memory Management Programming Guide 2011).
- (NSString*) methodName 
{ 
    NSString* s =  [[[NSString alloc] initWithString:@”Some text”] autorelease]; 
    /*
    Some operation
    */
    return s;
}
One important point to consider is that only objects created by alloc, new, copy, or mutableCopy should be released manually. For example, an object defined as follows will not need to be sent a release or autorelease message.
NSString* s = [NSString stringWithString:@”Some text”];
In fact the definition of object “s” above is similar to the following definition:
NSString* s = [[[NSString alloc] initWithString:@”Some text”] autorelease];

Conclusion
On the whole, by following some certain rules, practical memory management in Objective-C can be none or less challenging.
  1. Using accessors, that are written based on what is discussed, to get or mutate instance variables, as having retain and release on them throughout the code makes it harder to keep their reference-count track.
  2. @property notation defines both of the accessors in a standard and safe way. All needed, is to synthesize the methods by @synthesize notation on properties.
  3. All of the instance variables should be sent their final release message in dealloc method of the class.
Complying with the rules mentioned above, memory allocated to instance variables is being managed properly. The only matter to take into account is local variables which should be explicitly released, by either release or autorelease message, whichever is appropriate based on the context, if and only if they are created using alloc method.

References