Introduction
I have included a rationalization for each proclamation, so you get an idea, that some thought went into the design. In general it may be helpful to have understood what Gestalt Psychology is about and have skimmed an introductory text on Cognitive Psychology.The idea behind these guidelines is that they should make the code easier to read and to modify. The effect should be to save yourself mental effort in the long run, ease copy/pasting and debugging.
As these are guidelines they proclaim a certain ideal to aspire to. I find that I often write code, that violates a few rules. But after some refinement the code almost invariably turns into the form that is described here. This happens mostly, when some code is reexamined after a period of time. Blind adherence to the one loop per function/method rule has never turned out badly for me.
If I had the time and money, I'd verify some of the hypotheses or look up current research results. In the current form the guidelines are not (yet) scientific.
These style guides are based on Apple's WebKit Coding Style Guidelines. They have been modified greatly and now they are very much disparate.
Indenting
- Use spaces to indent. Tabs should not appear in source code files.
- The indent size is 3 spaces.
- Code editors should be configured to expand tabs that you type to 3
spaces.
Rationale
As current monospaced font characters are approximately 2 and 1/2 times as high as they are wide, the use of a three space indentation will produce a naturally centered block, when the vertical offset to the enveloping block is one line.xxxxXxxxxxxxxxxxxx x // indent 3 X OOO // pixel distance to both Xs about the same for first O xxxxxXxxxxxxxxxxx x // indent 4 X OOOO // pixel distance to left X is larger than to upper X
Logical divisions and Column Style
- Try to organize your code into vertical columns as much as possible and useful. For example:
RIGHT: double foobar; float foo; foo = sqrt( 2 * 2); foobar = foo * some_function( foo); WRONG: float foo; double foobar; foo = sqrt( 2 * 2); foobar = foo * some_function( foo);As a general rule, if you have to break up column style, separate the lines with an empty line. For example:RIGHT: fooIsUsuallyAVeryShortIdentifierButNotInThisCase = sqrt( 2 * 2); foobar = foo * some_function( foo); WRONG: fooIsUsuallyAVeryShortIdentifierButNotInThisCase = sqrt( 2 * 2); foobar = foo * some_function( foo);Rationale
As in a spreadsheet the column style code makes it easy to pick out pieces of your code that share a common fate (like assignment). It is for code reading very important to know where a variable is assigned or declared. Column Style makes the lookup quicker. - Objective-C Calls with more than one parameter should be broken up column style.
RIGHT: [self call:this with:that]; WRONG: [self call:this with:that];Rationale
Parameters are easily discerned from the method selector and vice versa. - In general try to separate your code into logical blocks. Take a hint from UI design, that writing more than 7 lines of code without separation is not a good idea and not so easy to parse for your brain. If your function does more than seven lines of code (excluding declarations, braces, comments and empty lines) consider wrapping them into a static inline function. It might be the right thing and it may be not!
Rationale
Abstraction and tokenization of more complex code can be a good thing if it makes the code simpler to understand. For trivial code it can break up the flow and might even be detrimental. An example would be the writing of 20 lines of text messages to stdout. - There must not be two empty lines in a function or method definition.
Rationale
This would break the visual offset from the next function or method.
Declarations
-
Order your declarations in some way, do not make them random. For example:
RIGHT: char *s; // sorted by type, OK double value; int bar; void foo(); int bar; // sorted by identifier, OK void foo(); char *s; double value; WRONG: double value; // looks nice but what's the use ? void foo(); char *s; int bar; void foo(); // random int bar; char *s; double value;Rationale
The random ordering does not make finding the variable any easier, quite the opposite. Sorting by name might be preferred, as you are probably looking at the declaration to find it's type. -
Do not put more than one variable after the type declaration. An exception to the rule is the customary pair unsigned int i, n;. For example:
RIGHT: char *s; double value; unsigned int i,n; WRONG: char *s, *q, *t;Rationale
This is a copy/paste issue. Removal and reordering of variables is now a matter of selecting whole lines and cutting them. Adding is often also just cut and paste with a little edit. - Declarations of local variables should only appear at the top of each block so don't mix declarations with logic statements.
RIGHT: { char *s; double value; s = "foo"; value = 0.0; WRONG: { char *s; s = "foo"; printf( "%s\n," s); double value = 0.0;Rationale
During debugging, local variables do not drop out of scope. Has a tendency to break column style code. - Avoid initialization of local variables at the time of declaration. You must not use non-constant initializers at declaration time.
WRONG: char *s = "foo"; double value = 0.0; COMPLETELY WRONG: char *s = malloc( 5); double value = sin( 0.2);Rationale
Initialization and declaration time tends to produce code, that is run but never used. A mix of declaration with statements is not very nice to debug.
Simplicity
- Write simple code. Do not nest function and method calls. Do not call the same function or method a second time, when the return value will be the same (get out of the functional programming trap). Use local variables. Try to avoid overwriting local variables. For example:
RIGHT: s = [self someString]; stripped = [s stringWithStuffStrippedOff]; NSLog( @"The result of %@ is %@", s, stripped); WRONG: NSLog( @"The result of %@ is %@", [self someString], [[self someString] stringWithStuffStrippedOff]); WRONG: s = [self someString]; s = [s stringWithStuffStrippedOff]; NSLog( @"The result of %@ is %@", [self someString], s);Rationale
The use of local variables makes the code much easier to debug. If you don't reuse local variables, it is easier to look in the past, at the time a crash occurs. Last but not least, caching function and method return values make your code much smaller and faster. -
The magic number for parameters and local variables is up to five or maybe seven. If your function or method for example needs to take more than five parameters consider wrapping them together in a context structure or its own class or an NSDictionary. If your function or method uses more than seven local variables, consider splitting it up into smaller pieces. For example:
typedef struct { NSString *info; NSDecimalNumber *number; NSNumber *another; int count; NSException *exception; } call_context; call_context ctxt; ctxt.info = @"bla"; ctxt.number = [NSDecimalNumber numberWithUnsignedInt:1848]; ctxt.another = [NSNumber numberWithInt:1]; ctxt.count = 1; ctxt.exception = nil; doStuff( self, &ctxt);Rationale
This adapts the code to the way the brain handles tokens in short term memory. The number five for parameters is really more an aesthetic thing, but seven is usually considered the amount of tokens the human brain can handle comfortably. Most of the time when many variables are used, they share a common fate and will be passed around to more functions. This will therefore likely reduce code size and increase performance. - One loop per function or method. static inline functions will not incur any overhead in release code. If the code inside the loop is non-trivial make it a separate function.
Rationale
This is somewhat forced on you, if you take the magical numbers seriously, as each loop is bound to use up some local variables. - Avoid complex return statements. Return only local variables and constants.
RIGHT: s = [self someString]; stripped = [s stringWithStuffStrippedOff]; return( stripped); // the parentheses are just a mannerism WRONG: s = [self someString]; return( [s stringWithStuffStrippedOff]);Rationale
It's easier to debug when you're not suddenly out of the function.
Braces
- Function definitions - open and close braces should be on lines by themselves. Do not put the open brace on the same line as the function signature. For example:
RIGHT: void foo() { } WRONG: void foo() { }There are two lines space between function definitions. For example:RIGHT: } void foo() { } WRONG: } void foo() { } - Loop control structures, including for, while and do statements - the open brace should go on the next line aligned with the control structure.
RIGHT: for( i = 0; i < n; i++) { } WRONG: for( i = 0; i < n; i++) { } - If/else statements - as above.
RIGHT: if( timeToGetCoffee) { buyCoffee( &coffee); chugIt( coffee); } else if( timeToGoHome) { outtaHere = true; } WRONG: if (timeToGetCoffee) { buyCoffee(&coffee); chugIt(coffee); } else if (timeToGoHome) outtaHere = true;Rationale
There is only one pattern the brain has to match to figure out if something is a block or not. Together with the three space offset discussed above, all blocks look similar as they are horizontally and vertically offset the same from the enveloping block and are surrounded by braces. Braces that don't match up are easily discovered.Copy/Paste of code is facilitated as the whole block can be copied by selecting the lines from opening brace to closing brace.
Operators and Parentheses
- As a general rule there is always a space between the open parenthesis and the first non-white character, except if the parentheses are empty. In function declarations and calls there will also be spaces after each comma that separate arguments.
Operators are separated by a space from their operands.
RIGHT: int some_function( int arg1, float arg2); void noArgFunction(); x = y + ! ! 1; WRONG: int some_function (int arg1, float arg2); // space before '(' int other_function( int arg1,float arg2 ); // no space after , void noArgFunction( ); // space in empty () x=y+!!1; // no spaces !Rationale
Operators are important, values and identifiers are important. Syntax is unimportant. Your brain should be able to discern them quickly. Now ALLWORDSRUNTOGETHER is harder to read than ALL WORDS RUN TOGETHER, that's why we use spacing in the first place. But how much harder to read is ALL:WORDS:RUN:TOGETHER or ALL.WORDS.RUN.TOGETHER ? Not very much, because punctuation has very little "ink".And another rule. It is easier to read a word with trailing garbage than it is to read one with leading garbage. @while vs. while@. The reason being, that as you read from left to right the top-down and bottom-up processing of a familiar identifier is usually already done by the brain, before the garbage is encountered.
Therefore parentheses spacing should be as in 2.:
- foo(bar) foo and bar run together, bad
- foo( bar) the least of all evils: trailing parentheses
- foo (bar) bar is de-emphasized by leading '(', not so good
- foo ( bar) undue emphasis on first '('
- foo ( bar ) undue emphasis on both '('
Labels
- Labels of case statements, goto statements and enveloping exception macros usually start on the parent indentation level:
RIGHT: void foo() { NS_DURING switch( n) { case 1 : { continue; } case 2 : break; default : ; goto done; } done:; NS_HANDLER NS_ENDHANDLER } WRONG: void foo() { NS_HANDLER switch( n) { case 1 : { // braces should be indented continue; } case 2 : // should not be indented break; default : goto done; // goto should be indented } done:; // should be on level of parent NS_HANDLER NS_ENDHANDLER }
Names
- General Rule: With very few exceptions, prefer embedded capitals (StudlyCaps) for object oriented code and underscore C style names for non object oriented code. A C function that expects an object as one its parameters is to be considered object oriented code in this respect.
- C++ and Objective-C classes, interfaces and protocols, and other
type names - these names should start with a capital letter and use
StudlyCaps.
RIGHT: class AnImportantClass WRONG: class An_important_class class anImportantClass - Try to keep local variables names short, as to avoid the use of StudlyCaps, but use StudlyCaps for all variables that refer to objects. You may use underscore C style variable names for pure C code. The first word should
start with a lowercase letter, like this:
RIGHT: id anId; WRONG: id AnId; id an_id; - Objective-C methods should follow the usual Cocoa naming style - they should read like a phrase or sentence and each piece of the selector should start with a lowercase letter and use StudlyCaps.
- Objective-C instance variables should be named like local
variables but are postfixed with an underscore, except if binary compatibility with Apple code specifies otherwise.
RIGHT: IBOutlet NSTextField *instanceVar_; WRONG: IBOutlet NSTextField *instanceVar; int _anotherInstanceVar; - Pointer and reference types - pointer types should be written without
a space between the variable name and the *. The same goes for reference types, the & goes next to the variable name.
RIGHT: IBOutlet NSTextField *foo; WRONG: IBOutlet NSTextField * instanceVar; char* foo; - Enum members should use StudlyCaps with an initial capital letter.
- #defined constants should use all uppercase names with words separated by underscores.
- Macros that take parameters could be named like functions or like normal #defines constants, your choice. There is very little use for macros with parameters nowadays, as they are almost always better coded as static inline functions.
- Acronyms in names: If an identifier includes an acronym, make the
acronym all-uppercase or all-lowercase, depending on whether a
word in that position would be capitalized or not.
RIGHT: urlVariable defaultURLAccessor: WRONG: uRLVariable defaultUrlAccessor: - The Prefix my is reserved for user code.