I have recently read Apple’s sample code for MVCNetworking written by Apple’s Developer Technical Support guru Quinn “The Eskimo!”. The sample is really nice learning experience with what I guess are best development practices for iOS development.
What surprised me, coming from JVM languages, are extremely frequent assertions like this:
syncDate = [NSDate date];
assert(syncDate != nil);
and this:
photosToRemove = [NSMutableSet setWithArray:knownPhotos];
assert(photosToRemove != nil);
and this:
photoIDToKnownPhotos = [NSMutableDictionary dictionary];
assert(photoIDToKnownPhotos != nil);
Is that really necessary? Is that coding style worth emulating?
If you’re used to Java, this may seem strange. You’d expect an object creation message to throw an exception when it fails, rather than return
nil. However, while Objective-C on Mac OS X has support for exception handling; it’s an optional feature that can be turned on/off with a compiler flag. The standard libraries are written so they can be used without exception handling turned on: hence messages often returnnilto indicate errors, and sometimes require you to also pass a pointer to anNSError*variable. (This is for Mac development, I’m not sure whether you can even turn exception handling support on for iOS, considering you also can’t turn on garbage collection for iOS.)The section “Handling Initialization Failure” in the document “The Objective-C Programming Language” explains how Objective-C programmers are expected to deal with errors in object initialization/creation: that is, return
nil.Something like
[NSData dataWithContentsOfFile: path]may definitely returnnil: the documentation for the method explicitly says so. But I’m honestly not sure whether something like[NSMutableArray arrayWithCapacity: n]ever returnsnil. The only situation I can think of when it might is when the application is out of memory. But in that case I’d expect the application to be aborted by the attempt to allocate more memory. I have not checked this though, and it may very well be that it returnsnilin this case. While in Objective-C you can often safely send messages tonil, this could then still lead to undesirable results. For example, your application may try to make anNSMutableArray, getnilinstead, and then happily continue sendingaddObject:toniland write out an empty file to disk rather than one with elements of the array as intended. So in some cases it’s better to check explicitly whether the result of a message wasnil. Whether doing it at every object creation is necessary, like the programmer you’re quoting is doing, I’m not sure. Better safe than sorry perhaps?Edit: I’d like to add that while checking that object creation succeeded can sometimes be a good idea, asserting it may not be the best idea. You’d want this to be also checked in the release version of your application, not just in the debug version. Otherwise it kind of defeats the point of checking it, since you don’t want the application end user to, for example, wind up with empty files because
[NSMutableArray arrayWithCapacity: n]returnedniland the application continued sending messages to thenilreturn value. Assertions (withassertorNSAssert) can be removed from the release version with compiler flags; Xcode doesn’t seem to include these flags by default in the “Release” configuration though. But if you’d want to use these flags to remove some other assertions, you’d also be removing all your “object creation succeeded” checks.Edit: Upon further reflection, it seems more plausible than I first thought that
[NSMutableArray arrayWithCapacity: n]would returnnilrather than abort the application when not enough memory is available. Basic Cmallocalso doesn’t abort but returns aNULLpointer when not enough memory is available. But I haven’t yet found any clear mention of this in the Objective-C documentation onallocand similar methods.Edit: Above I said I wasn’t sure checking for
nilis necessary at every object creation. But it shouldn’t be. This is exactly why Objective-C allows sending messages tonil, which then returnnil(or0or something similar, depending on the message definition): this way,nilcan propagate through your code somewhat similar to an exception so that you don’t have to explicitly check fornilat every single message that might return it. But it’s a good idea to check for it at points where you don’t want it to propagate, like when writing files, interacting with the user and so on, or in cases where the result of sending a message tonilis undefined (as explained in the documentation on sending messages tonil). I’d be inclined to say this is like the “poor man’s” version of exception propagation&handling, though not everyone may agree that the latter is better; butnildoesn’t tell you anything about why an error occurred and you can easily forget to check for it where such checks are necessary.