Code Review

One of the major negatives to programming on your own, especially with a new language or framework, is the lack of an external input to your learning process.

Other People’s Code

I have been learning Cocoa and Objective-C for over a year part-time and on my own. I have learned enough to get by and be reasonably productive.

Recently, I finished up a full-time C# consulting contract and have been working full-time in Cocoa and Objective-C. I’ve made huge leaps forward in my understanding of Apple’s development frameworks now that I’m concentrating on one language for most of the day.

What helped the most to improve my grasp of Apple’s powerful frameworks was taking on a part-time Cocoa development contract. Aside from example code from a few outdated Cocoa books, Apple’s documentation, and a few open source projects; this was the first time I had the opportunity to view complex production-ready Cocoa source code.

The problem with proactively searching help and example code is you have to know what to search for and that you even need to look. Reviewing other people’s code gives you an insight on better ways to do the things you thought you were already doing correctly.

What do I mean? Perhaps an oversimplified Cocoa example would help.

Finding out how to log trace messages was easy. I opened up help in Xcode and searched for logging. The third entry found was “Logging Messages” showing this example:

int recNum;
NSString *recName;
/* ... */
NSLog( @"Record %d is %@", recNum, recName );

So, I’ve been calling NSLog all over the place to help track method execution and the order event messages are called. Here is how I was logging my trace messages:

- (void)foo
{
	NSLog(@"foo");
}

I had a problem. Every time I had to rename a method signature, I was having to change my NSLog trace messages. Five minutes of looking at my client’s code and I learned a new trick that will save me countless hours of time going forward when re-factoring code:

- (void)foo
{
	NSLog(@"%s", _cmd);
}

Searching through Cocoa documentation, I found references to __FUNCTION__ and __PRETTY_FUNCTION__ for C/C++ but I didn’t find anything for Objective-C. So, I moved on. Maybe, I didn’t look hard enough.

Code Review

This morning, my client sent me an email titled “code review“. “Oh no”, I thought to myself. I’m going to get fired because I’m doing something seriously wrong.

But, what I received was an incredibly generous and detailed review of the code I had written so far. Some of the comments were simple style suggestions, but the bulk of the feedback contained invaluable insights in to best practices for Cocoa Bindings and CoreData from someone who has several years of Cocoa development experience over me and a successful and selling commercial product.

I realize my client has a self-serving reason behind reviewing my code and giving me feedback. The better my code, the better his product will be. But, this feedback can not be gained working by yourself on your own code.

It’s easy to forget how important outside influence on your learning process really is. We (here I mean me) tend to think we can learn anything with a good instructional book and some example code. Up to a point, I think this is true; but having a mentor in this business is invaluable.

8 thoughts on “Code Review

  1. Thanks Scott. “Hidden” does makes me feel a bit better.

    You are quite the TextMate pusher lately. I’ll have to download it and give it a try.

  2. Pingback: Outer Level » Blog Archive » Cocoa Blogs

  3. Joeroen: I’m currently using the TextMate demo and trying to give a good honest try. So far I really like it for HTML work. I’m using it to author my help documentation for my upcoming release of LicenseKeeper. For Cocoa work, I’m having a harder time stepping away from Xcode.

  4. For Cocoa work, I’m having a harder time stepping away from Xcode

    Google for ‘textmate objective-c screencast’. I think it might make the difference.

  5. __FUNCTION__ and __PRETTY_FUNCTION__ are fine and can be used — they are C niceties and as Obj-C is a superset, they can be used — just remember they are type const char *

    I use them in debug code to define a basic class with a macro to all the log

    As such:

    —–MyLog.h
    // some loglevels — your labels would vary
    #define MTLogAlways 0
    #define MTLogUpdates 1
    #define MTLogLibraryUpdates 2
    #define MTLogSearches 4

    @interface MyLog : NSObject
    {
    }
    +(void)logWithOptions:(int)options file:(char*)sourceFile function:(char*)functionName lineNumber:(int)lineNumber format:(NSString*)format, …;

    #define MyLog(opts,s,…) [MyLog logWithOptions:opts file:__FILE__ function: (char *)__FUNCTION__ lineNumber:__LINE__ format:(s),##__VA_ARGS__]
    @end
    —– MyLog.m
    static int __MyLogLevel;

    @implementation MyLog
    +(void)initialize
    {
    __MyLogLevel= [[[NSUserDefaults standardUserDefaults] objectForKey:@”logLevel”] intValue];
    // you can store logLevel as a hidden preference — this would be a bitwise and to add logging for different parts of your code.

    }
    +(void)logWithOptions:(int)options file:(char*)sourceFile function:(char*)functionName lineNumber:(int)lineNumber
    format:(NSString*)format, …{

    if(options == 0 || __MyLogLevel & options ){
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    va_list ap;
    NSString *print,*file, *function;
    va_start(ap,format);
    file=[[NSString alloc] initWithBytes:sourceFile
    length:strlen(sourceFile)
    encoding:NSUTF8StringEncoding];
    function = [NSString stringWithCString:functionName];
    print=[[NSString alloc] initWithFormat:format arguments:ap];
    va_end(ap);
    NSLog(@”%@:%d; %@”,function,lineNumber ,print);
    [print release];
    [file release];

    [pool release];

    }
    return;
    }

    @end

    —–

    now to log in code

    MyLog(MTLogAlways,@”Hello %@”,aString);

    would output
    x.x.x.x myapp[1234] -[myClass mytestmethod]:5; Hello world
    assuming aString was @”world”

    whereas
    MyLog(MTLogUpdates,@”Hello world”);
    would only log if the first bit of the logLevel pref is on. (ie logLevel & 1 = 1)

    Note that I am cleaning this up from my code — it may not be working 100% but you should get the idea…

Comments are closed.