-
Notifications
You must be signed in to change notification settings - Fork 164
Code style guide
Kod generally follows the Google Objective-C Style Guide.
They say an example is worth a thousand words so let's start off with an example that should give you a feel for the style, spacing, naming, etc.
An example header file, demonstrating the correct commenting and spacing for an @interface
declaration
// A sample class demonstrating good Objective-C style. All interfaces,
// categories, and protocols (read: all top-level declarations in a header)
// should be commented. Comments must also be adjacent to the object they're
// documenting.
//
// (no blank line between this comment and the interface)
@interface KFoo : NSObject {
NSString *foo_;
NSString *bar_;
}
// Returns an autoreleased instance of KFoo. See -initWithString: for details
// about the argument.
+ (id)fooWithString:(NSString *)string;
// Designated initializer. |string| will be copied and assigned to |foo_|.
- (id)initWithString:(NSString *)string;
// Prefer @properties to traditional getters/setters.
@property(assign) NSString *foo;
// Label for the xyz
@property(assign) NSString *bar;
// Does some work on |blah| and returns YES if the work was completed
// successfuly, and NO otherwise.
- (BOOL)doWorkWithString:(NSString *)blah;
@end
An example source file, demonstating the correct commenting and spacing for the @implementation of an interface. It also includes the reference implementations for important methods like getters and setters, init, and dealloc.
#import "KFoo.h"
@implementation KFoo
// Prefer to use synthesized property implementations
@synthesize foo = foo_;
+ (id)fooWithString:(NSString *)string {
return [[[self alloc] initWithString:string] autorelease];
}
// Must always override super's designated initializer.
- (id)init {
return [self initWithString:nil];
}
- (id)initWithString:(NSString *)string {
if ((self = [super init])) {
foo_ = [string copy];
bar_ = [[NSString alloc] initWithFormat:@"hi %d", 3];
}
return self;
}
- (void)dealloc {
[foo_ release];
[bar_ release];
[super dealloc];
}
- (NSString *)bar {
return bar_;
}
- (void)setBar:(NSString *)bar {
[bar_ autorelease];
bar_ = [bar retain];
}
- (BOOL)doWorkWithString:(NSString *)blah {
// ...
return NO;
}
@end
Since Kod is limited to the Xcode platform and the two compilers GCC and Clang, we prefer the use of #import
rather than #include
. If you do use #include
, make sure to add a header guard in the style described below.
All non-objective-C header files should begin with a #pragma once
to prevent multiple inclusion.
Don't use an #import or #include when a forward declaration would suffice.
When you include a header file you introduce a dependency that will cause your code to be recompiled whenever the header file changes. If your header file includes other header files, any change to those files will cause any code that includes your header to be recompiled. Therefore, we prefer to minimize includes, particularly includes of header files in other header files.
You can significantly minimize the number of header files you need to include in your own header files by using forward declarations. For example, if your header file uses the File
class in ways that do not require access to the declaration of the File
class, your header file can just forward declare class File
; instead of having to #import "foo/file.h"
.
The most important consistency rules are those that govern naming. The style of a name immediately informs us what sort of thing the named entity is: a type, a variable, a function, a constant, a macro, etc., without requiring us to search for the declaration of that entity. The pattern-matching engine in our brains relies a great deal on these naming rules.
Naming rules are pretty arbitrary, but we feel that consistency is more important than individual preferences in this area, so regardless of whether you find them sensible or not, the rules are the rules.
Function names, variable names, and filenames should be descriptive; eschew abbreviation. Types and variables should be nouns, while functions should be "command" verbs.
Give as descriptive a name as possible, within reason. Do not worry about saving horizontal space as it is far more important to make your code immediately understandable by a new reader. Examples of well-chosen names:
int num_errors; // Good.
int numCompletedConnections; // Good.
Poorly-chosen names use ambiguous abbreviations or arbitrary characters that do not convey meaning:
int n; // Bad - meaningless.
int nerr; // Bad - ambiguous abbreviation.
int n_comp_conns; // Bad - ambiguous abbreviation.
Type and variable names should typically be nouns: e.g., FileOpener
, num_errors
.
Function names should typically be imperative (that is they should be commands): e.g., openFile
, setNumErrors
. There is an exception for accessors, which, described more completely in Function names, should be named the same as the variable they access.
Do not use abbreviations unless they are extremely well known outside your project. For example:
// Good
// These show proper names with no abbreviations.
int num_dns_connections; // Most people know what "DNS" stands for.
int price_count_reader; // OK, price count. Makes sense.
// Bad!
// Abbreviations can be confusing or ambiguous outside a small group.
int wgc_connections; // Only your group knows what this stands for.
int pc_reader; // Lots of things can be abbreviated "pc".
Never abbreviate by leaving out letters:
int error_count; // Good.
int error_cnt; // Bad.
A file containing a class should match that classs name e.g. a class named
FooBarshould be defined in the file
FooBar.hand implemented in
FooBar.mm`.
For files containing other content, use all lower case characters and _
(underscore) characters and give it a descriptive name. For instance, a file containing symbols for virtual key codes and functions for manipulating or converting those the name virtual_key_codes.h
is appropriate.
Prefer class member functions to global functions; use completely global functions rarely.
Place a function's variables in the narrowest scope possible, and initialize variables in the declaration.
C and C++ allows you to declare variables anywhere in a function. We encourage you to declare them in as local a scope as possible, and as close to the first use as possible. This makes it easier for the reader to find the declaration and see what type the variable is and what it was initialized to. In particular, initialization should be used instead of declaration and assignment, e.g.
int i;
i = f(); // Bad -- initialization separate from declaration.
Instead:
int j = g(); // Good -- declaration has initialization.
Note that gcc and clang implements for (int i = 0; i < 10; ++i)
correctly (the scope of i is only the scope of the for loop), so you can then reuse i in another for loop in the same scope. It also correctly scopes declarations in if and while statements, e.g.
while (const char* p = strchr(str, '/')) str = p + 1;
There is one caveat: if the variable is an object on the stack, its constructor is invoked every time it enters scope and is created, and its destructor is invoked every time it goes out of scope.
// Inefficient implementation:
for (int i = 0; i < 1000000; ++i) {
Foo f; // My ctor and dtor get called 1000000 times each.
f.DoSomething(i);
}
It may be more efficient to declare such a variable used in a loop outside that loop:
Foo f; // My ctor and dtor get called once each.
for (int i = 0; i < 1000000; ++i) {
f.DoSomething(i);
}
Classes are the fundamental unit of code in Objective-C and C++. Naturally, we use them extensively. This section lists the main dos and don'ts you should follow when writing a class.
In general, C++ constructors should merely set member variables to their initial values. Any complex initialization should go in an explicit Init() method.
You must define a default constructor if your class defines member variables and has no other constructors. Otherwise the compiler will do it for you, badly.
Use the C++ keyword explicit for constructors with one argument.
Use a struct only for passive objects that carry data; everything else is a class.
Composition is often more appropriate than inheritance. When using inheritance in C++, make it public.
Prefer small and focused functions.