This is my style. This guide has two parts:
- The philosophy
- The cookbook (aka the reference): If-this-then-do-that.
My style is heavily influenced by:
- https://webkit.org/code-style-guidelines/
- https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html
- K&R
- FreeBSD style guide
As I type code I think about:
- How it relates to the code above and (if already written) below it.
- Whether the code should stand out or not.
Whitespace is one of the ways I use to address these things. I use whitespace before and after code to group related lines into paragraphs. That does not always mean I put an empty line after an if/while/for/etc-statement. It also means sometimes there is only 1 paragraph in a function or struct.
Unless the language mandates it to produce correct code gen, I use whitespace instead of braces.
The right way to do software engineering is to pick the right structures for the code being written. There are control structures (e.g. if, while, do, for, switch) and data structures (e.g. arrays, unions, etc). Finding the right structure is a skill, but here are 2 different general strategies I use when I can't identify it from the get-go:
Top down:
- Free flow write code and get a working solution.
- Review each line of code, each assigment, each control flow statement, etc etc and ask: a. Am I repeating myself? If so, see (b). b. Is there an another language structure that would be better suited for what is being done? If so, use it.
Bottom up:
- Solve the problem using the absolute mimimum number of machine instructions and memory footprint (I find this exercise to be a fun game). This tends to produce a very clever solution that would require comments, but I don't want to add comments if the code can be made intutive by using other langauge structures. So, I...
- Work backwards from (1) replacing low level primitives with higher level ones until the solution no longer needs comments.
Once I have the right structures in place then it's time to consult the cookbook!
Note that portions of this section were taken in whole or in part from <https://webkit.org/code-style-guidelines/>.
-
Use spaces, not tabs. Tabs can be used where they have semantic meaning, like in string and character literals:
static const char kTabCharacter = ' ';
-
Use 4 spaces for indentation.
Right:
static BOOL myFunction()
{
return YES;
}
Wrong:
static BOOL myFunction()
{
return YES;
}
- A case label should line up with its switch statement. The case statement is indented.
Right:
switch (condition) {
case fooCondition:
case barCondition:
i++;
break;
default:
i--;
}
Wrong:
switch (condition) {
case fooCondition:
case barCondition:
i++;
break;
default:
i--;
}
- Boolean expressions at the same nesting level that span multiple lines should have their operators on the left side of the line instead of the right side.
Right:
return [attribute.name isEqualToString:srcAttr]
|| [attribute.name isEqualToString:lowsrcAttr]
|| ([attribute.name isEqualToString:useMapAttr] && [attribute.value.string characterAtIndex:0] != '#');
Wrong:
return [attribute.name isEqualToString:srcAttr] ||
[attribute.name isEqualToString:lowsrcAttr] ||
([attribute.name isEqualToString:useMapAttr] && [attribute.value.string characterAtIndex:0] != '#');
- Do not place spaces around unary operators.
Right:
i++;
++i;
Wrong:
i ++;
++ i;
- Do place spaces around binary and ternary operators.
Right:
z = x * x + y * y;
f(a, b);
c = a | b;
return condition ? 1 : 0;
return condition ?: nil;
Wrong:
z=x*x+y*y;
f(a,b);
c = a|b;
return condition ? 1:0;
return condition ?:nil;
- Do not place spaces before comma and semicolon.
Right:
for (int i = 0; i < 10; ++i)
doSomething();
f(a, b);
Wrong:
for (int i = 0 ; i < 10 ; ++i)
doSomething();
f(a , b) ;
- Do place spaces between control statements and their parentheses.
Right
if (condition)
doSomething();
Wrong:
if(condition)
doSomething();
- Do not place spaces between a function and its parentheses, or between a parenthesis and its content.
Right
f(a, b);
Wrong:
f (a, b);
f( a, b );
- Do not place spaces between the start of a block and its arguments, or the start of a block and its opening brace.
- Do place a space between argument lists and the opening brace of the block.
Right:
checkState = ^{
...
};
checkState = ^(int, int) {
...
};
Wrong:
checkState = ^ {
...
};
checkState = ^ (int, int) {
...
};
-
Each statement should be on its own line because:
a. This makes it easier to see the control flow of the program.
b. Because of (a) it is easier to set breakpoints on each statement.
c. It helps make sequences (i.e. things that use the comma operator e.g.
x = (a = b, a * a)
) stand out more by making statements stand out less, which is good because sequences tend to be harder to read and reason about.
Right:
x++;
y++;
if (condition)
f();
Wrong:
x++; y++;
if (condition) f();
- An
else
statement should go on the same line as a preceding close brace if one is present, else it should line up with theif
statement.
Right:
if (condition) {
...
} else {
...
}
if (condition)
doSomething();
else
doSomethingElse();
if (condition)
doSomething();
else {
...
}
Wrong:
if (condition) {
...
}
else {
...
}
if (condition) doSomething(); else doSomethingElse();
if (condition) doSomething(); else {
...
}
- An
else if
statement should be written as anif
statement when the priorif
concludes with areturn
statement.
Right:
if (condition) {
...
return someValue;
}
if (condition) {
...
}
Wrong:
if (condition) {
...
return someValue;
} else if (condition) {
...
}
Place each brace on its own line.
Right:
int main()
{
...
}
Wrong:
int main() {
...
}
Place the open brace on the line preceding the code block; place the close brace on its own line.
Right:
struct MyStruct {
...
};
for (int i = 0; i < 10; ++i) {
...
}
Wrong:
struct MyStruct
{
...
};
for (int i = 0; i < 10; ++i)
{
...
}
Should not use braces unless comments are included or a single statement spans multiple lines.
Right:
if (condition)
doIt();
if (condition) {
// Some comment
doIt();
}
if (condition) {
myFunction(reallyLongParam1, reallyLongParam2, ...
reallyLongParam5);
}
Wrong:
if (condition) {
doIt();
}
if (condition)
// Some comment
doIt();
if (condition)
myFunction(reallyLongParam1, reallyLongParam2, ...
reallyLongParam5);
Right:
for ( ; current; current = current->next) { }
Wrong:
for ( ; current; current = current->next);
-
In Objective-C++, the null pointer value for a C++ type should be written as
nullptr
. For a C type, it should be written asNULL
. For an Objective-C object it should be written asnil
. -
Objective-C
BOOL
values should be written asYES
andNO
. -
In Objective-C, instance variables are initialized to zero automatically. Don’t add explicit initializations to nil or NO in an init method.
-
Tests for true/false, null/non-null, and zero/non-zero should all be done without equality comparisons:
Right:
if (condition)
doIt();
if (!ptr)
return;
if (!count)
return;
Wrong:
if (condition == YES)
doIt();
if (ptr == NULL)
return;
if (count == 0)
return;
Unless required in order to force floating point math, do not append .0
, .f
and .0f
to floating point literals.
Right:
const double duration = 60;
- (void)setDiameter:(float)diameter
{
_radius = diameter / 2;
}
[self setDiameter:10];
const int framesPerSecond = 12;
double frameDuration = 1.0 / framesPerSecond;
Wrong:
const double duration = 60.0;
- (void)setDiameter:(float)diameter
{
_radius = diameter / 2.f;
}
[self setDiameter:10.f];
const int framesPerSecond = 12;
double frameDuration = 1 / framesPerSecond; // integer division
- Use properties instead of manually declaring setters and getter and defining instance variables.
Right:
@interface Document
@property (nonatomic) Frame *frame;
@property (nonatomic) NSURL *url;
@property (nonatomic, readonly) NSString *title;
@end
Wrong:
@interface Document {
Frame *frame;
}
- (NSURL *)url;
- (void)setURL:(NSURL *)url;
- (NSString *)title;
@end
- Declare private and implementation-specific variables in an anonymous category in the implementation file or instead of declaring in the class definition.
Right:
// Document.h
@interface Document
@property (nonatomic) NSURL *url;
@end
// Document.m
@interface Document ()
@property (nonatomic) Frame *frame;
@end
Wrong:
// Document.h
@interface Document {
@private
Frame *frame;
}
@end
- Sort case statements unless they cascade. For case statements that cascade, sort the cascade.
Right:
switch (condition) {
case A:
...
break;
case B:
...
break;
case C:
...
break;
}
switch (anotherCondition) {
case C:
...
break;
case A:
case B:
...
break;
}
Wrong:
switch (condition) {
case C:
...
break;
case B:
...
break;
case A:
...
break;
}
switch (anotherCondition) {
case C:
...
break;
case B:
case A:
...
break;
}
- Avoid mixing cases that
break
andreturn
. That is, prefer having all casesbreak
orreturn
.
Right:
switch (condition) {
case A:
...
break;
case B:
...
break;
case C:
...
break;
}
Wrong:
switch (condition) {
case A:
...
break;
case B:
...
return;
case C:
...
break;
}
- Omit the default statement whenever the condition is an enum type.
Right:
switch (condition) { // condition is of type enum { A, B, C }.
case A:
...
break;
case B:
case C:
...
break;
}
switch (anotherCondition) { // condition is of type unsigned
case 1:
...
break;
case 2:
case 3:
...
break;
default:
...
break;
}
Wrong:
switch (condition) { // condition is of type enum { A, B, C }.
case A:
...
break;
default:
...
break;
}
- Use
[[fallthrough]]
in cases that have non-empty body that cascade.
Right:
switch (condition) {
case A:
doSomething();
[[fallthrough]];
case B:
doSomethingElse();
break;
}
Wrong:
switch (condition) {
case A:
doSomething();
case B:
doSomethingElse();
break;
}
- Guard against bad casts when the condition is an enum type and all cases
return
by adding an assert outside the switch block and returning some default value.
Right:
NSString *statusCodeDescription(StatusCode code)
{
switch (code) { // code is of type enum { StatusCodeNoFile, StatusCodeNoPermission }.
case StatusCodeNoFile:
return @"File not found";
case StatusCodeNoPermission:
return @"Operation not permitted";
}
ASSERT_NOT_REACHED();
return @"";
}
Wrong:
NSString *statusCodeDescription(StatusCode code)
{
switch (code) { // code is of type enum { StatusCodeNoFile, StatusCodeNoPermission }.
case StatusCodeNoFile:
return @"File not found";
case StatusCodeNoPermission:
return @"Operation not permitted";
}
return @"";
}
Use auto_type for the type of the left-hand-side expression.
Right:
__auto_type checkState = ^(NSString *aString) {
...
return YES;
};
Wrong:
BOOL (^checkState)(NSString *) = ^BOOL(NSString *aString) {
...
return YES;
};
Right:
typedef CheckStateFunctionType = BOOL (^checkState)(NSString *)
Sometimes to write a test a part of the implementation needs to be exposed (i.e. encapsulation must be broken). Using these functions outside of testing can make it more difficult in the future to change the implementation of a class because callers may intentionally or unintentionally become dependent on such knowledge. The right way to add a testing-only function is to:
-
Append "ForTesting" to the end of its name. This makes it easy for other developers, code reviewers and even automated tools to spot (by name alone) usage of it outside of testing.
-
Declare it an _Internal.h or _Private.h file or some other testing-only header.
This way it is not exposed in any public API headers for embedding clients to become dependent on.
Right:
- (NSData *)bufferByNameForTesting:(NSString *)name;
- (void)setSlowScrollingEnabledForTesting:(BOOL)enabled;
Wrong:
- (NSData *)bufferByName:(NSString *)name;
- (NSData *)bufferForTestingByName:(NSString *)name;
- (void)_setSlowScrollingEnabled:(BOOL)enabled;