Development: Coding Style Guidelines

Coding Style Guidelines

When several style choices are possible, the only rule is to be consistent (within each module). In other words, pick one rule and stick to it unless you contribute to a module already using another style.
Beginning by a language remark… Name your variables, classes, methods, functions and all other things in American English. Comments should likewise be in English, but your own local English is fine (as long as it's readable to anyone fluent in any dialect of English).

Preliminary note

Various parts of this document have been extracted from Adium HACKING document, DragonFlyBSD style man page and cocoadevcentral.com articles on Objective-C Style.

Encoding and Wrapping

Always encode text in UTF-8, except with 'strings' files which should be encoded in UTF-16.

Use a 80 characters wide wrapping within source code or raw text files such as README or INSTALL. You can rely on automatic wrapping for end user documentation stored in the repository or elsewhere.

Terminology Style

  • Pay attention to Étoilé capitalization and accentuation in documentation, web site etc., though it's perfectly valid and even recommended to write Etoile in this way without é letter in source code (either code or comment)
  • Don't forget about proper GNUstep capitalization
  • Talk about Mac OS X and not MacOSX or MacOS X

Project Hierarchy Style

A project is usually separated in several directories:

  • one or more directories that group related source code
    • headers files
    • implementation files
  • Tests directory (optional, contains the test suite)
    • headers files
    • implementation files
  • English.lproj directory (and other localization directories)
    • image files
    • nib or gorm files
    • plist files
    • etc.
  • Documentation directory (optional, can be created by documentation.make automatically)
  • Examples directory (optional)
  • README.md
  • INSTALL.md (or both INSTALL.GNUstep.md and INSTALL.Cocoa.md)
  • NEWS.md
  • TODO.md
  • COPYING (or LICENSE)
  • GNUmakefile

For an example, check the CoreObject repository.

When the project is fairly small (few headers and source code files), the source code can sit directly in the project directory.

Files

The project directory must contain the following files formatted in a similar way:

Source Code Style

Updating Code to Current Style

Any changes to existing files should be consistent with that file's conventions. In general, code can be considered new code when it makes up about 50 % or more of the file(s) involved. This is enough to break precedents in the existing code and use the current style guidelines.

Indenting

Use both tabs and spaces to indent in a semantic way. Practically, use tab to indent blocks, then if you have to adjust your indenting furthermore use spaces (most of time, it is the result of the line breaks introduced by the 80 characters length limit).

This allows you to modify the tab size yet retaining layout like this (note that this will only work in a fixed with font):

[object doSomethingWith: this           // This is the first argument
                andAlso: anotherThing]  // This is the second

tabs...[object doSomethingWith: this           // This is the first argument
tabs...spaces..........andAlso: anotherThing]  // This is the second

The rational is to have the colons and comments line up whatever the user sets their tab width to.
If we use spaces, then the only way a user can change the indent width is to replace a sequence of 4 spaces with n spaces (or a tab), which will destroy this kind of method arguments alignment.
If we just use tabs, then the same applies (it's slightly easier to change the indent width, but we still lose alignment).

By using this combination, we let to developers flexibility as to how they view the indenting, but still retain a clear and readable body of code.

C Macros

Preprocessor related declarations like macro names are all in uppercase, every words within being separated by an underscore.

Example:

#define WHATEVER_NUMBER 5

C Statements

Put a single space after control statement keywords (if, do, while, for, switch).

Correct

if (a)

Bad

if(a)

Don't pad the inside of the parentheses with spaces.

Correct

if (a && b)

Bad

if ( a && b )

Don't use parentheses around the argument to return (making it look like a function call).

Correct

return 0; // followed by a space and its single argument shouldn't be enclosed in parentheses

Bad

return(0);

But it's OK for parenthesized expressions:

return ((x * y) ? 42 : [array objectAtIndex:fallback]);

Do not add whitespace at the end of a line, and only use tabs followed by spaces to form the indentation.

Closing and opening braces go on their own line (never inline the opening brace on the first statement line). Braces that are not necessary may be left out, but always use braces around compound, complex or confusing sequences.

Leave all blank lines (those with no visible characters) empty (not containing any characters). This means that a blank line should not contain any tabs or spaces.

Flow Statement Examples

Correct

         if (test) 
         {
                 stmt;
         } 
         else if (bar) 
         {
                 stmt;
                 stmt;
         } 
         else 
         {
                 stmt;
         }

Bad

        if (test)
                 stmt;
         else if (bar)
                 stmt;
         else
                 stmt;

Bad

         if (test) {
                 stmt;
         } else if (bar) {
                 stmt;
                 stmt;
         } else {
                 stmt;
         }

Border cases

         /* THIS IS CORRECT, BRACES ARE OPTIONAL FOR TWO LINE STATEMENTS */
         if (test) 
                 stmt;


         /* BUT THIS IS WRONG */
         if (test) whatever;


         /* THIS IS WRONG, BRACES SHOULD BE USED */
         if (fubar)
                 /* xyz */
                 x = 1;


         /* THIS IS ALSO WRONG, USE BRACES AROUND THE OUTER CONDITIONAL */
         if (fubar)
                 if (barbaz)
                         x = 1;

C Operators

Unary operators do not require spaces around them, but binary operators (except for . and ->) do.

Correct

2 + 2, x && y, x << 256

Bad

2+2, x&&y, x<<256

Try to use parentheses unless the precedence rules are evident or unless the statement isn't confusing without them. Remember that other people may become confused more easily than you. Do YOU understand the following?

a = b->c[0] + ~d == (e || f) || g && h ? i : j >> 1;
k = !(l & FLAGS);

Casts are not followed by a space. Also treat sizeof as a function, but never return. In other words, return is followed by a space and its single argument shouldn't be enclosed in parentheses.

nil is the preferred Objective-C null pointer constant, NULL is the one for C data structures. Use nil or NULL instead of 0 or (type *)0, (type *)NULL.

Test pointers against NULL or nil in the more readable way.

Correct

 if (object == nil) //  or if (object != nil)

Bad

 if (object) // or if (!object)

Try to prefer the operator == to the != when it makes sense.

Global Symbols (Structures, Enums, Constants, Protocols)

Global symbols should be declared at the top of the header file matching the implementation file in which they are used, or in a separate header if they are used in multiple source files. When the symbol is used in a private way, you can declare it at the top of your implementation file.

It is recommended to use a typedef for public structures or enums. They must have a name matching the struct/enum tag.

Example_

 /* Make the structure name match the typedef. */
 typedef struct _bar 
 {
         int     level;
 } bar;

Global C Functions

You should avoid to create such C functions most of time, but when you think they are really welcome, name them in this way:

  • Prefix + Value ()
  • Prefix + Value + With/From/For/In + Input ()

Examples

  • EXPersonForAddress(address)
  • EXPersonsForStreetInAddressBook(street, book)
  • EXPersonFromName(name) // may return a new person

  • Prefix + Action ()

  • Prefix + Action + Type ()
  • Prefix + Action + With/From/For/In + Type ()

Examples

  • EXRemoveAddress(address)
  • EXRemoveDuplicateForAddress(address)
  • EXRemovePersonWithAddress(address)
  • EXRemovePersonInAddressBook(person, book)

All functions are prototyped somewhere. Functions local to one source module should be declared static.

Don't put a space between a function name and the opening parenthesis.

Correct

printf("%s", "Hello world!\n");

Bad

printf ("%s", "Hello world!\n");

Inside Objective-C Methods or C Functions

Do not put spaces after function names, after '(' or '[' characters, or preceding ']', ')', ';', or ',' characters. But do put a space after commas and semicolons if there is further text on the same line.

Example

         error = function(a1, a2);
         if (error != 0)
                 exit(error);

// Improve this section with Objective-C examples.

Classes

You should always choose class names very carefully in order to keep a wide view of your project relatively easy by just looking at used class names. Here is the right way to compose a class name:

  • Prefix + Name

Every words should be capitalized in a class name like in IKIconProvider, and the class name should be prefixed with two or three letters in order to avoid name collisions (because Objective-C has no namespaces support). For example, Foundation framework includes a class named NSString for 'string' related class, to create a custom string class or subclass in a framework HappyKit you would call it HKString. In this way you avoid collisions which would occur if two classes where referenced by String name in the runtime.

Finally when you create a subclass, unless it makes no sense, think to reuse the superclass name by prefixing it with a name which conveys in a straightforward way the intent of the new subclass.

Variables

Always initialize variables at declaration time or immediately afterwards. Never use a variable's value without initializing it first.

Variable names start with a word in lower case letter, when more than one word is used in the name, these next words have their first letter capitalized. Underscores and digits should be avoided in variable names, albeit there is one exception: when the variable is an ivar (instance variable) and used in an Étoilé framework it can possibly start with an underscore.

Explicit and distinct variable names must be preferred to short ones, abbreviated names should be absolutely avoided except for recurrent variable like a counter i in a loop.

Correct

NSString  *hostName;
NSNumber *ipAddress;
NSArray *accounts;

Bad

NSString  *HST_NM;
NSNumber *theip;
NSArray *acc;

Finally, most of time with very common classes type information shouldn't be embedded in variable names.

Correct

NSString *accountName;
NSMutableArray *mailboxes;
BOOL userInputWasUpdated;

Bad

NSString *accountNameString;
NSMutableArray *mailboxArray;
BOOL userInputWasUpdatedBOOL;

However NSURL *hostURL or NSDictionary *documentsDictionary would be an acceptable name.

Finally with other classes when the variable name isn't self-explanatory, you should suffix the variable name with its related class name. For singleton objects, their variable name could be identical to their class names.

Examples

NSImage *buddyImage;  // buddy alone isn't self-explanatory, we may use 'Picture' too
NSProgressIndicator *uploadIndicator;
NSFontManager *fontManager; // only one, we have a singleton, we may reuse class name

Parameters

Let's take a quick visit to method parameters. Typically you prefix the input name with the, a, an or new. You have the possibility to use no prefix when the parameter is related to a ivar (instance variable) prefixed by an underscore, this happens in Étoilé frameworks with setter methods.

The correct way to put spaces around a parameter in a method is:

  • First Method Keyword + space + Type + Parameter Name + space + Next Method Keyword

Example

setTitle: (NSString *)newTitle withEncoding: (otherType)encoding

Note: For methods with a least one parameter, any method keywords end with :.

Example:

- (void)setName:  (NSString *)newName;
- (void)setTitle: (NSString *)aTitle;
- (id)keyForOption: (CDCOption *)anOption;
- (NSArray *)emailsForMailbox: (CDCMailbox *)theMailbox;
- (CDCEmail *)emailForRecipients: (NSArray *)theRecipients;

// Bad vs Correct examples to be added here

Method/Message Naming

Objective-C methods must be named in an expressive and clear way, to rely on carrefully thought method wording is a key part of great Objective-C code.

GNUstep (or Cocoa) programmers think from the end, choosing a method name based on how it will look in actual use. Let's say I want to have an in-memory file object written to disk. In some languages, that would look like this:

fileWrapper.write(path, true, true);

In GNUstep/Objective-C, it looks like this:

[fileWrapper writeToFile: path atomically: YES updateFilenames: YES];

When creating a method, ask yourself if its behavior will be clear from its name alone, particularly when surrounded by tens of thousands of lines of code.

Programmers do much more reading of code than writing, so Objective-C and GNUstep are designed to read well. Reading a message as a phrase is a good way to test your method name:

// "open the file with this application and deactivate"
[workspace openFile: mailing withApplication: @"MailDrop" andDeactivate: YES];

This message is sent to NSWorkspace, and it clearly passes the "phrase" test.

Naming Objective-C methods is a tricky and lengthy topic, we won't discuss it here. If you are interested by some advices, read this article Cocoa Style for Objective-C: Part 2.

Finally always include return type, even if it's id.

Correct

- (id)title;

Bad

- title;

Method/Message Layout

In Étoilé, you should use spaces around in a method declaration in the way depicted on the next line.

  • Plus or Minus + space + Return Type + First Method Keyword + space + Type + Parameter Name + space + Next Method Keyword etc. + semicolon

Correct

- (void)setTitle: (NSString *)newTitle withEncoding: (otherType)encoding;
+ (NSString *)title: (NSString *)newTitle withEncoding: (otherType)encoding;

Bad

- (void)setTitle:(NSString *) newTitle withEncoding:(otherType) encoding;
+(NSString *) title:(NSString *)newTitle withEncoding:(otherType)encoding;

An exception to the indentation rules for Objective-C, when it's possible we don't break long methods by indenting subsequent lines by a tab, but make the parts of the method line up instead. The way to do this is indent so the colons line up. More about it in Indenting.

Example

[receiver doSomethingWith: firstArg                         
                      and: secondArg
                     also: thirdArg];

When the method keywords are not really numerous yet quite long, you can prefer to break the lines before a parameter exceeds the 80 characters line length. Here is an example:

[receiver doSomethingAndAnotherThingWith: firstArg withAnotherTrickyValue: secondArg
    inTalktativeParameterContext: myLastVerboseParameter];

Similarly, you must not break a method in the midst of a method keyword and its related parameter, but between keyword/parameter pairs.

// Bad vs Correct example to be added here.

Accessors

You must create accessors when an instance variable (ivar) is going to be accessed by another object than the one the ivar is belonging to. In fact, it is advised to rely on accessors internally too, in other words when an object want to know or update its own state.

Here is the proper way to spell read and write accessors:

  • (valueType)valueName; /* this is a getter */
  • (void)set + ValueName: (valueType)parameter /* this is a setter */

Take note, you must avoid the get prefix in your getter names.

Correct

 - (EXAddress *)address

Bad

 - (EXAddress *)getAddress

The only exception is when you're returning a value indirectly via a memory address, use get prefix.

Example

/* Copy objects from the NSArray to the buffer */

id *buffer = (id *) malloc(sizeof(id) * [array count]);
[array getObjects: buffer];

To know which is the best way to name setter parameter, read the section Parameters.

Messages/Methods Arrangement

Methods should be arranged in the following order (very important rule when you write a header):

  • Class methods
  • Factory methods
  • Initialization methods
  • Properties
  • Main methods (logic, behavior related)

The method order in the implementation must be the same than in the header.

Here is an example:

@interface DummyClass : MySuperclass

/* Class and Factory Methods */

+ (Foo *)classMethods;

+ (id)classMethodsThatCreateObjects;

/* Initialization */

- (id)init;
- (id )initWithArgument: (id)anArgument;

// Equality and description methods are usually put here

/* Properties */

// The getter is just before the setter
- (id)value;
- (void)setValue: (id)newValue;

/* Main Methods */

- (void)doSomething;

@end

Comments

/*
 * VERY important single-line comments look like this.
 */

/* Most single-line comments look like this. */

/*
 * Multi-line comments look like this.  Make them real sentences. Fill
 * them so they look like real paragraphs.
 */

/* Another kind of multi-line comments
   which is allowed. */

When you want to indicate code which is incomplete, suboptimal or otherwise deserving of further attention, the comments should be written double slashed.

// XXX: I'm a nasty bug which needs to be phased out quickly.

XXX should be usually replaced by one of the following words:

  • FIXME indicates bug, temporary workaround or missing code necessary to have a usable implementation

  • TODO indicates necessary refactoring, welcome optimisation or missing code to have a complete implementation

  • NOTE indicates implementation details or subtleties which are important in order to maintain code or refactor it

    // NOTE: Don't forget to break such double slashed comment in multi-lines manner // when the current line is wider than 80 characters.

Patterns Code Style

  • Singleton.
    Name the singleton provider method in this way:
    • shared + Instance (preferred way)
    • shared + ClassName (eventually use the traditional GNUstep/Cocoa way)

Various References and Links