My friend Demitri showed me a coding technique he uses when he has a bunch of lines that need to be preceded by a "start" statement of some sort and followed by an "end" statement of some sort.
A recent example in my own code looks roughly like this:
[self startOutput]; // top-level "start"
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
[self endOutput]; // top-level "end" |
[self startOutput]; // top-level "start"
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
[self endOutput]; // top-level "end"
The problem is that the "start" and the "end" may be far enough apart that it's not visually apparent that the lines in between are logically nested. In some cases, though not so much in this example, it can be easy to forget to add the "end" statement.
One approach would be to wrap all the interior stuff into a method, so it collapses into one line:
[self startOutput];
[self outputAllAssets];
[self endOutput]; |
[self startOutput];
[self outputAllAssets];
[self endOutput];
This follows the excellent principle of Keeping Methods Short, and now the beginning/middle/end sequence is clear. But this isn't always convenient, and I don't always prefer it over inlining the intermediate statements.
I've seen code that indents the internal statements like this:
[self startOutput];
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
[self endOutput]; |
[self startOutput];
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
[self endOutput];
I didn't indent the "add" statements because each of those groups of three lines is short enough that it's obvious what's going on, and I think indenting the middle lines would make the code harder to read, at least to my eye.
The problem with this approach is that when you tell Xcode to Re-Indent, you will lose all your custom indentation. (Side note: I'm not sure, but it seems to me "Re-Indent" in the Xcode menu should be "Re-indent".)
Demitri's solution is to put the internal statements into a C block, like this:
[self startOutput];
{
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
}
[self endOutput]; |
[self startOutput];
{
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
}
[self endOutput];
This works nicely, but one teeny-weeny thing about it bothers me. When I see that opening brace it feels like I'm missing a line, like maybe I accidentally deleted an "if" or a "while". I could probably get used to this, especially since the "start" in the line above is a strong clue. But just to help myself see my intentions, I've taken to using double curly-braces:
[self startOutput];
{{
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
}}
[self endOutput]; |
[self startOutput];
{{
[self startImageAssets];
[self addImageAssets];
[self endImageAssets];
[self startThumbnailAssets];
[self addThumbnailAssets];
[self endThumbnailAssets];
[self startAudioAssets];
[self addAudioAssets];
[self endAudioAssets];
}}
[self endOutput];
Now when I scan the code I can clearly see that the reason for the block was to indicate logical nesting.
This technique can also be useful for Create/Release pairs in CF code, or alloc/release pairs in Cocoa or iOS code. Or for those times when you create your own autorelease pool:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
{{
// Autoreleased objects here go into the local pool.
}}
[pool drain]; // Less likely you'll forget to drain the pool. |
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
{{
// Autoreleased objects here go into the local pool.
}}
[pool drain]; // Less likely you'll forget to drain the pool.
I believe the example Demitri showed me was a begin/commit animation block in his iOS code. With the double braces it would look like this:
[UIView beginAnimations:animationID context:context];
{{
// Add an animation to the animation block.
// Add an animation to the animation block.
// ...
}}
[UIView commitAnimations]; |
[UIView beginAnimations:animationID context:context];
{{
// Add an animation to the animation block.
// Add an animation to the animation block.
// ...
}}
[UIView commitAnimations];
One concern is that someone else reading my code might be confused if they're not familiar with the convention I adopted. Ideally it would turn out I wasn't the first to think of the double-braces approach, and actually lots of people do it this way. But I haven't come across it. Maybe most people take the first approach I mentioned and create a separate method or function.
I think of the double braces sort of like the double parens in "old-style" init methods (which are not how Apple does them any more, but perfectly fine technically):
if ((self = [super init]))
{
// ...
} |
if ((self = [super init]))
{
// ...
}
The double parens avoid a warning from the compiler that you might have meant == instead of =. My double braces avoid a "mental compiler warning" that I might be missing an "if" or a "while".