Over a million developers have joined DZone.

10 More 'Most Useful' iPhone Tips (Part 2)

· Mobile Zone

 This post is a continuation of my 3 previous posts:

Let's get rolling.


6. Restoring application's state

Three20 framework can restore view controllers if you stick to TTNavigator and passing all data as URL parameters. But imagine you have a complex object to pass between views, for example search criteria, or you'd like to store email of the last successful login. You can combine Three20 restore view controllers functionality with Apple's provided NSUserDefaults class.
When application is starting and terminating proper UIApplicationDelegate methods are called. These methods are perfect places for restoring/persisting application state. To illustrate this situation take a look at the following code:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    // restore app's state from NSUserDefaults
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSData *data = [defaults objectForKey:@"searchCriteria"];
    NSKeyedArchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
    SearchCriteria *searchCriteria = [[SearchCriteria alloc] initWithCoder: unarchiver];
    [DataContainerService setSearchCriteria:searchCriteria];

    NSString *lastSuccessfulLoginEmailAddress = [defaults objectForKey:@"lastSuccessfulLoginEmailAddress"];
    [DataContainerService setLastSuccessfulLoginEmailAddress:lastSuccessfulLoginEmailAddress];
    
    [TTStyleSheet setGlobalStyleSheet:[[[DefaultStyles alloc] init] autorelease]];
    
    TTNavigator* navigator = [TTNavigator navigator];
    navigator.persistenceMode = TTNavigatorPersistenceModeAll;
    navigator.supportsShakeToReload = YES;
    navigator.opensExternalURLs = YES;

    TTURLMap* map = navigator.URLMap;

    [map from:@"*" toViewController:[TTWebController class]];
    [map from:@"tt://tabBar" toSharedViewController:[TabBarController class]];
    [map from:@"tt://search" toSharedViewController:[SearchViewController class]];
    [map from:@"tt://map" toSharedViewController:[MapViewController class]];

    if (![navigator restoreViewControllers]) {
        [navigator openURLAction:[TTURLAction actionWithURLPath:@"tt://tabBar"]];
    }
    
    return YES;
}

- (void)applicationWillTerminate:(UIApplication *)application {
    SearchCriteria *searchCriteria = [DataContainerService searchCriteria];
    
    NSMutableData *data = [NSMutableData new];
    NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
    [propertySearchCriteria encodeWithCoder:encoder];
    [encoder finishEncoding];
    
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:data forKey:@"searchCriteria"];
    [data release];
    
    NSString *lastSuccessfulLoginEmailAddress = [DataContainerService lastSuccessfulLoginEmailAddress];
    [defaults setObject:lastSuccessfulLoginEmailAddress forKey:@"lastSuccessfulLoginEmailAddress"];

}
7. Use sizeToFit

Many UI controls (like UILabel, UIButton) offer auto-resizing functionality. This can be used when populating dynamically text from JSON, etc. To resize the label simply invoke [label sizeToFit] method and you're at home.

8. Dynamic buttons with userInfo property

Sometimes, depending on the design of the view, you can end up with many buttons doing the same thing but with a different object.

For example imagine a ward list for hospital application. There are like X dynamically created buttons with patients' names, when you tap a button, view with patient's details appears. Normally you would assign all buttons the same action and within it you would iterate over the collection of buttons find touched button's index, retrieve the patient from patient's collection, then based on patient's id you would open details view.

This can be simplified, no collections for patients and buttons is required if you extend UIButton and add userInfo property. Take a look:
@interface UserInfoButton : UIButton {
    id _userInfo;
}
@property (nonatomic, assign) id userInfo;
@end

@implementation UserInfoButton
@synthesize userInfo = _userInfo;
@end
And in view controller:
for (Patient *patient in patients) {
    UserInfoButton *button = [[UserInfoButton alloc] initWithFrame:CGRectMake(0, offset, 155, 15)];
    [button addTarget:self action:@selector(navigateToPatientDetailsView:) forControlEvents:UIControlEventTouchUpInside];
    button.userInfo = patient;
    ...
}


and in action:

-(void) navigateToPatientDetailsView: (UserInfoButton*) sender {
    TTOpenURL([NSString stringWithFormat:@"tt://patient/%d", ((Patient*)sender.userInfo).patientId]);
}
Simple, isn't it?

9. Use JSON to exchange data

If you exchange data with external Web Services it's much better to use RESTful Web Services and JSON.

Why? There are 2 main reasons:

1. RESTful Web Services can be easily cached on both client and server side.
2. JSON data format is much compact when compare to quite verbose XML

There are numerous JSON libraries for Objective-C. Just pickup your favourite one.

10. Beware of autoreleased objects, release object just after you finish working with them

Autorelead objects are nightmares. Why you should beware of them? Because they can get autoreleased anytime (if you don't retain them). You won't spot this issue while developing your application, it will be spotted when, in system or user acceptance tests, your application will be running for more than a few seconds.

In most cases it's very easy to know if an object returned by a method is autoreleased or not as Apple's got a naming convention which tells you that. If a method creates an object and its name starts with the name of the object (without UI or NS prefixes) then it means that the returned object is autoreleased. Examples are:
[NSString stringWithFormat:@"I'm just an example: %@", text];
[UIImage imageNamed:pathToImage];
Also, be aware of releasing all objects which you create. The best place to release properties is -(void)dealloc method. Local variables should be released the very moment they are no longer needed.

And finally, remember that arrays retain references. The following source code:
NSMutableArray *array = [NSMutableArray new];
for (NSUInteger i = 0; i < 5; i++) {
   NSString *newString = [[NSString alloc] initWithFormat: @"I'm string no %@", (i+1)];
   NSLog(@"Before adding string to array '%@', its retain count is %d", newString, [newString retainCount]);
   [array addObject: newString];
   NSLog(@"After adding string to array '%@', its retain count is %d", newString, [newString retainCount]);
}
NSString *lastInsertedString = [array lastObject];
[array release];
NSLog(@"Last object in array is '%@', its retain count is %d", [lastInsertedString retainCount]);

Results in a memory leak:

Before adding string to array 'I'm string no 1', its retain count is 1
After adding string to array 'I'm string no 1', its retain count is 2
Before adding string to array 'I'm string no 2', its retain count is 1
After adding string to array 'I'm string no 2', its retain count is 2
Before adding string to array 'I'm string no 3', its retain count is 1
After adding string to array 'I'm string no 3', its retain count is 2
Before adding string to array 'I'm string no 4', its retain count is 1
After adding string to array 'I'm string no 4', its retain count is 2
Before adding string to array 'I'm string no 5', its retain count is 1
After adding string to array 'I'm string no 5', its retain count is 2
Last object in array is 'I'm string no 5', its retain count is 1
What you should do is to add [newString release]; inside the loop to release newly created object just after they got added to the array. Then if you rerun the above code the last line will result in bad access. Why? because the lastInsertedString object got finally released!

Bonus tip: Map Kit estimates

Map Kit... When estimating anything related to Map Kit multiply it by 2 or even 3 if you plan to do a full customisation of callouts, pins, grouped pins, packing/unpacking pins, GPS integration, etc. Map Kit is a ... forget it, just stick to the defaults or prepare yourself for the worst :)

Thanks,
Łukasz

Topics:

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}