Returning nil from init (Part 2)

16 Aug

After writing about checking for nil being returned from init, a friend pointed out that I omitted nullable annotations, as well as possibly documented usage. They also raised an interesting point about failing (crashing) early vs partially recovering – given the contrived button example.

Let’s address nullability first. I ignored mentioning nullability annotations as there is a lot of existing code in the wild that hasn’t adopted them. (If you’re writing new code you absolutely should be using them). With nullability annotations, the question of if you should check for nil being returned from init is already answered. If the API specifics it is nullable – check. Otherwise you can assume you’ll get a valid initialized object. Even with nullable annotations there’s no runtime guarantee (it’s a static analyzer warning), but you should trust the API, if it doesn’t perform as advertised – that’s a bug.

As for documentation – you are writing header docs right? Docs can, and should, describe if initialization can fail, and under which conditions that might occur. If the API you’re calling doesn’t have nullability annotations, check the docs. You might find that it’s clearly documented if initialization can fail, and if you should be checking for nil.

You should absolutely be writing header docs by the way. Your team will love you for it. Even if you’re a solo developer; future you will thank you for it.

Finally, onto the topic of crash early, catch early. I used a button class as a completely arbitrary example last time. I could have, and perhaps should have, picked a better example. It’s extremely unlikely that you’d ever want a button to have a failable initializer. But it did raise and interesting question: If a button did actually return nil – what’s the “correct” behavior? Is it better to guard against crashing, or to guard against a broken UI? I think it would depend on a number of factors – what conditions in which the crash might occur, how important the button is to the functionality, is the app doing critical work that needs to be stopped safely? During development I’d be ok with permitting a crash. You might not notice that one in a dozen times the button happened to be missing, but you’d notice a crash right away.

All this is to say, when getting a value back from an initializer, stop and think for just a second, could this fail? What happens if it does? How should we react?

Returning nil from init

13 Aug

I was recently asked why we should bother checking for nil in the code below? Shouldn’t init always return an object?

NSMutableArray *buttons = [[NSMutableArray alloc] init];
LMActivityButton *activityButton = [[LMActivityButton alloc] init];
if (activityButton != nil) {
    [buttons addObject:activityButton];
}

Nope. It’s actually perfectly valid to return nil from an initializer. In fact, if initialization fails it should return nil. It’s the preferred way to indicate initialization failed.

You’ve been (hopefully) partially adhering to this convention without even thinking about it. Every time you type the following bit:

SomeClass *someObject = [[SomeClass alloc] init];

Instead of

SomeClass *someObject = [SomeClass alloc];
[someObject init];

The second version is unsafe, because init could return nil and release its memory (or substitute an entirely different object).

Now, we could look at the source for LMActivityButton above and see that it doesn’t actually implement an initialization method; it defaults to NSObject’s init. Knowing that NSObject doesn’t actually do anything in init, it just returns self, one could argue the check for nil is unneeded. And today, one would be correct. However, nothing says that LMActivityButton won’t add its own custom initializer in the future, and that said initializer won’t return nil to indicate failure – though admittedly it would be highly unlikely / unexpected for a button to do something so complex as to fail initialization.

For me, this really comes down to good habits. It’s a good habit to check for nil from an initializer if you’re going to be doing work that expects it to be non-nil, like adding it to an array. It also serves to remind you, and others, that init can fail, and that other, more critical, places in your code should protect against it.

Race conditions

1 Aug

Race conditions came up during a conversation I had about a week ago. I tried to explain them as best I could, but I failed horribly, metaphor piled on top of metaphor, I just couldn’t come up with a simple explanation.

A much clearer metaphor came to me today, so here goes:

I ask Charile to drive to the store. 
At the same time I ask Sally to go along and pick up something from the store. 

And just like that we’ve created a race condition. Sally getting to the store depends on her riding with Charile. I never told Charile to wait on Sally, so Charile gets in his car and drives away, leaving Sally stuck with no way to complete her task. 

Now Charile is usually slow getting ready, and thus slow to get to his car. This means Sally is usually there waiting on him. But every now and then, Charile has is keys in hand, ready and waiting.

So often Sally will make it to the store, but every so often she won’t and the system falls apart.

This is the most basic race condition. Two tasks (one driving, one riding) where one depends on the other, and it can be fixed simply enough. We just need to tell Charile to wait until Sally is ready, and then drive to the store.

I’d be interested to hear other people’s non-CS explanations for problems like race conditions, deadlocks, memory management, etc.