Saturday, April 27, 2013

Keeping the code clean of warnings helps your productivity

In some Fortune 500 companies I worked we had an official policy of 0 compilation warnings allowed in the code. That was a good practice that saved us of many troubles. Since then I try to follow that practice in my projects.

Let's see an example:

        NSLog(@"Hello, World!");
        
        int i = 1;
        switch (i) {
            case1:
                NSLog(@"OK");
                break;
        }

If I placed this code in a project with the default settings created by Xcode, the result wouldn't output the "OK" string, due to the "case1:" typo which transformed the "case 1:" case statement into an unused label. The compilation wouldn't produce any warning. Neither would the static analyzer.

You can however catch such problems at compile time, by changing the default settings like so:


This enables all warnings. The following setting treats all warning as errors:


If I now try to compile I get this error:


Catching this bug at compile time is much better than later when the app may have been released.

Another useful setting to have is this:

which runs the static analyzer on every compilation. I found this combination of settings very useful to smoke out bugs shortly after they have been introduced.





Saturday, April 20, 2013

Unexpected side effect of fast enumeration in Objective-C

Fast enumeration is a relatively new capability in Objective-C. For a thorough survey of all the ways one can enumerate through a collection, you can see this post.

In a recent project I had a code sequence like this (modified here for illustration):


        NSArray *array = @[@1, @2];
        NSNumber *number = nil;

        for (number in array) {
            NSLog(@"number: %@", number);
        }
        
        NSLog(@"Last number: %@", number);


My expectation was that at the end of the for loop the number variable would hold the last value of the array. Here's what I get instead:


2013-04-19 17:13:06.223 FastEnumBug[49016:303] number: 1
2013-04-19 17:13:06.240 FastEnumBug[49016:303] number: 2
2013-04-19 17:13:06.241 FastEnumBug[49016:303] Last number: (null)


Strangely the value comes back as nil. Which is what it was initialized to before entering the loop. Could that be the case ? Let's try this instead:

        NSArray *array = @[@1, @2];
        NSNumber *number = @100;

        for (number in array) {
            NSLog(@"number: %@", number);
        }
        
        NSLog(@"Last number: %@", number);

which produces:


2013-04-20 18:47:34.209 FastEnumBug[62468:303] number: 1
2013-04-20 18:47:34.212 FastEnumBug[62468:303] number: 2
2013-04-20 18:47:34.212 FastEnumBug[62468:303] Last number: (null)


Nope, I still get nil, not the @100 value at the entry of the loop. So it seems that fast enumeration blocks  reset the value of the iteration pointer in the outer context back to nil. This is, at least to me, unexpected. Maybe it's a bug. I'll see what Apple has to say.