Skip to content
newacct edited this page Nov 7, 2011 · 5 revisions

Advanced Logging

Description will go here.

Examples

Some examples of AppLogging will go here

A Word of Caution

The new logging feature in RestKit is quite powerful, and can bring a new level of logging utility to your application, far beyond what NSLog() will get you. Such power, however, must be used with a bit of caution. It's very tempting to simply run a find/replace of NSLog() for RKLogDebug(). The syntax of RKLogDebug/RKLogInfo/RKLogError (and so on) is fully compatible with NSLog(), so what's the harm? Us. What I mean to say is that if we were all extremely diligent coders, and were never lazy with brackets, we'd never see any issue. But, honestly, the reason why I (@grgcombs) am writing up this little addendum is because I'm far from perfect and often too lazy for my own good. Let me show you what I mean with some junky code I had lying around (in wait):

// just open the url, don't bother checking for network access
+ (BOOL) openURLWithoutTrepidation:(NSURL *)url {
    BOOL canOpenURL = NO;
    
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url];
        canOpenURL = YES;
    }
    else
        NSLog(@"Can't open this URL: %@", url);
    
    return canOpenURL;
}

Now, what happens if we replace that NSLog() with our new logging facility?

// just open the url, don't bother checking for network access
+ (BOOL) openURLWithoutTrepidation:(NSURL *)url {
    BOOL canOpenURL = NO;
    
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url];
        canOpenURL = YES;
    }
    else
        RKLogError(@"Can't open this URL: %@", url);	

    return canOpenURL;
}			

Well, that's fine, so long as RKLogSetAppLoggingLevel() is set sufficiently low (i.e. less than or equal to RKLogLevelError). But if it isn't, then can you guess what's going to happen? The RKLogError() line disappears from the compiled code, leaving a big gaping hole underneath the else, so the compiler will suck up the return canOpenURL since there isn't a set of brackets to tell it what to do. Luckily, since my method is supposed to actually return something, I saw the compiler warning (not an error!) that the control may reach the end of a non-void function. If your block was stuck in the middle of a longer method, you'll probably have some code that isn't going to execute like you thought it would. Who knows what will happen.

So, let this be a lesson to all of you... don't be lazy. All it takes is an extra couple of brackets and this never would've happened. It shouldn't to you, if you're reading this...

// just open the url, don't bother checking for network access
+ (BOOL) openURLWithoutTrepidation:(NSURL *)url {
    BOOL canOpenURL = NO;
    
    if ([[UIApplication sharedApplication] canOpenURL:url]) {
        [[UIApplication sharedApplication] openURL:url];
        canOpenURL = YES;
    }
    else {
        RKLogError(@"Can't open this URL: %@", url);	
    }
    
    return canOpenURL;
}			

The difference is subtle, but the importance warrants some extra caution. Do a Find All of NSLog() on your projects. My guess is you've got at least one or two of these little monsters creeping around too.

UPDATED - This issue is likely cured with some recent modifications to the logging system. I'll keep this here for now, though, as it's still valid advice (to use brackets on if/else/etc) when using any kind of #define posing as a function ... a condition may be present elsewhere that results in the omission of the statement, as it I noted previously.