Make Your Life Easier With Reusable Code

Matthew Campbell, March 3rd, 2009

If you have spent any amount of time learning or working with the iPhone SDK then you are probably getting frustrated with the very wordy (and difficult to remember) nature of the Cocoa framework. Things that are trivial in environments like .NET or Java can seem awkward and clumsy in Cocoa.

As an example of this wordiness, here is what I do to create a button in code:


UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectZero;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
[button addTarget:target action:selector forControlEvents:UIControlEventTouchUpInside];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
button.backgroundColor = [UIColor clearColor];
button.frame = CGRectMake(10, 10, 100, 50);
[button setTitle:@"Button" forState:UIControlStateNormal];

And here is what I routinely do to use a simple alert box (one line of code in .NET):


UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Help Message"
message:message
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles: nil];
[alert show];
[alert release];

To be fair, every programming language has issues like this to some extent and the purpose of this post is not to bash Cocoa. The purpose of this post is to suggest a way to make your life easier by learning how to reuse your code.

WARNING: There is a 50% chance that what I am about to suggest will deeply offend you!

There are a few different ways to make your code reusable – in object-oriented programming whole books have been written about this. What I am about to tell may offend both object-oriented purists as well as people who truly love the way Cocoa does things.

My suggestion is to simply pay attention to the code that you are writing over and over again; then put this code into static functions in a class that you can simply call from any other classes. Programming veterans will be familiar with this approach (and might as well skip to the code examples at this point).

So, if you put all that wordy code into a static function you can use that code in the future with only one line required to get the same behavior. This makes your code more readable and will decrease your time coding because you are not re-doing the same thing over and over again.

WHY WOULD SOMETHING SO COOL OFFEND PEOPLE?

This approach is basically creating global functions and sort of steps on the toes of good object-oriented design that insists that objects created from a class that have all the information and behavior required to get by in your program. Intermixing global functions doesn’t quite fit into this and could lead to the problem of “spaghetti code” if you are not careful or if you are excessive in this approach.

Other people simply believe that the Cocoa way is the best way and that essentially creating your own framework on top of Cocoa some form of denial system. Like most system design choices there are +/- and you will have to keep your thinking cap on.

HERE IS WHAT I DO

Personally, I use static functions to help me work with using the log since I hate using the NSString stringWithFormat nonsense. I also use it for alerts and buttons. In general, I try to keep the global static functions to very general things and keep anything else in the class where it belongs. For example, all database access functions I put into a SQLite object that just accesses data.

NSLOG EXAMPLE

I use NSLog a lot while debugging code and even though it only requires one line of code I find it awkward to use. So, I have created three static functions to automate this for me:

+(void) logThis:(NSString *) message;
+(void) logThis:(NSString *) message andThisFloat:(float) number;
+(void) logThis:(NSString *) message andThisObject:(id) object;

This makes it easy to shoot out a message to the log – either by itself, with number value (very common need) or the string value of any object.

Here I will show you the steps required to create these static functions so that you can do for yourself in the future.

First, open up your favorite XCode project and add a NSObject subclass file. You will have two files (one with a h file extension and one with a m file extension). You will need to edit both.

Open the header file (with the h!) and add this code:

#import
@interface ASFunctions : NSObject {
}
+(void) logThis:(NSString *) message;
+(void) logThis:(NSString *) message andThisFloat:(float) number;
+(void) logThis:(NSString *) message andThisObject:(id) object;
@end

Now, add this code to the implementation file (m):

+(void) logThis:(NSString *) message{
NSLog(message);
}
+(void) logThis:(NSString *) message andThisFloat:(float) number{
NSLog([NSString stringWithFormat:@"%@ = %f", message, number]);
}
+(void) logThis:(NSString *) message andThisObject:(id) object{
NSLog([NSString stringWithFormat:@"%@ = %@", message, object]);
}

Put this after the @implementation but before the @end. Ok, almost done. In order to use this from another part of the program you must add some code to the file you are working with. For instance, I am going to add these functions to my app delegate for simplicity. Add the reference to the header file to the top:

#import "ASFunctions.h"

Now all you need to do is simply call the functions as you need them. Since they are static you do not need to worry about allocating the object. Here is how to use the simplest log function I wrote:

 [ASFunctions logThis:@"Writin' to da log!"];

Using the other functions is similar:

 [ASFunctions logThis:@"Writin' to da log!" andThisFloat:4];

Well, that is it. These examples are almost too simple for this approach but I hope they give you some ideas about the code you would like to put into static functions.

Want some input about the future direction of this blog? Please fill out our survey!

What do YOU think? What functions make sense to be put into global static functions? What don’t? Should everything be object-oriented or what?

Comments

I’ve been enjoying the blog. Thanks for sharing! I agree that Cocoa is often pretty verbose but as for NSLog, you can actually write things like: NSLog(@”Logging an object: %@”, myObject); very much like printf() with addition of the %@ specifier which will print the object’s description (you can write your own – (NSString*) description method to return a custom string, just like Java’s toString() method!

bpenrod, March 9th, 2009

Has anybody actually expressed offense at this yet? Based on your examples, you’re just wrapping already static functions (because they’re plain C) or already class-level methods into class-level utility methods. I see nothing wrong with this–Apple already does it in so many of their frameworks–as long as it doesn’t eventually lead to being overly dependent on shoehorning all your calls into “simplified” methods. Your thinking for the logThis methods is great to have for a consistent log format in your app.

I’d recommend cleaning up your logThis methods, though. As @bpenrod points out, NSLog already behaves similarly to printf. First, you are creating a temporary NSString with stringWithFormat: that just adds overhead to reproduce NSLog’s formatting functionality; NSLog is already overhead enough without another temp object.

Second, calling NSLog with only a single parameter (as all your calls end up) can be dangerous. If either ‘message’ or ‘object’ contain a ‘%’ character, NSLog will try to interpolate the next parameter on the stack–which is undefined. Making your methods ‘NSLog(@”%@”,message,nil)’, ‘NSLog(@”%@ = %f”, message, number,nil)’ and ‘NSLog(@”%@ = %@”, message, object,nil)’ respectively should clean this up. (The nil isn’t necessary, I’m just paranoid and like the parallels with NSArray and NSDictionary methods.)

Wow, that got verbose for what’s really just a touch of polish. Keep up the good work.

pgor, March 12th, 2009