Objective-C coding standards. Coding

Coding

Organizing the Code
CO1. Each header file is self-contained.
If a header file is self-contained, nothing more than the inclusion of the single header file is needed to use the full interface of the class defined. 
Notes: One way to test your header file is to always include it first in the corresponding implementation file. 
Status: Rule


CO2. Each header file contains declaration of one class/interface/protocol only.
This makes easier to read your source code files. This also improves the version control of the files; for example the file containing a stable class declaration can be committed and not changed anymore. 
Exceptions: 
  • It is allowed to declare related delegates, data sources, and extensions to declared class in one file 
Status: Rule


CO3. Implementation files hold member function definitions for a single class as defined in the corresponding header file.
This is for the same reason as for CO2
Exceptions: 
The exceptions are the same as for CO2
Status: Rule


CO4. Avoid unnecessary inclusions.
This is necessary to guarantee that the dependencies present in the implementations are only those foreseen in the design. 
Example: 
// File Y.h 
#import "X.h" 

// File Z.h or Z.m 
#import "X.h" // Not needed, avoid this inclusion
#import "Y.h"

Status: Recommendation


CO5. Use forward declaration instead of including a header file, if this is sufficient.
Example: 
@class HPLLine; 

@interface HPLPoint 

//... 
- (NSInteger)distanceFromLine:(HPLLine *)aLine; 

@end

Here it is sufficient to say that HPLLine is a class, without giving details which are inside its header. This saves time in compilation and avoids an apparent dependency upon the corresponding header file. 
Status: Recommendation

Control Flow
CF1. Do not change a loop variable inside a for loop block.
When you write a for loop, it is highly confusing and error-prone to change the loop variable within the loop body rather than inside the expression executed after each iteration. 
Example: 
for (NSInteger theIndex = 0; theIndex < kHPLMaxIndex; ++theIndex)
{
   // ...
   // Wrong
   theIndex -= 19 + x;
}

Status: Rule


CF2. All switch statements have a default clause.
In some cases the default clause can never be reached because there are case labels for all possible values in the switch statement, but by having such an unreachable default clause you show a potential reader that you know what you are doing. You also provide it for future changes, since if an additional value is added, the switch statement should not just silently ignore the new value. 
Example:
- (id)responseObject
{
//...
switch (self.responseType) 
{
case kHPLResponseTypeJSON:
Unknown macro: { //... break; }
case kHPLResponseTypePlist:
case kHPLResponseTypeString:
Unknown macro: { //... break; }
case kHPLResponseTypeCSV:
case kHPLResponseTypeXML: // fall through: treat XML as data
case kHPLResponseTypeData:
Unknown macro: { //... break; }
default:
Unknown macro: { // unforeseen response type; it is a defect // to do some action here break; }
}
//...
}
Notes: The fall-through feature the switch statement is be commented additionally to simplify future maintenance.
Status: Rule 

CF3. Do not use goto.
Use break or continue instead. This statement remains valid also in the case of nested loops, where the use of control variables can easily allow to break the loop, without using goto. 
Status: Rule


CF4. Do not have overly complex functions.
The number of possible paths through a function, which depends on the number of control flow primitives, is the main source of function complexity. Thus heavy use of control flow primitives make a code more difficult to maintain. As a rule of thumb, use the 7±2 rule: typically methods should not be longer than 7±2 statements. 
Status: Recommendation

Object Life Cycle: Construction/Initialization and Copying
CL1. Declare constant variables initialized to numeric values or strings in a highly visible position.
It's really hard to maintain a code in which numeric values or strings are spread over a big file. That's why declaration and initialization of constant numeric and string variables are to be placed at the beginning of header/implementation file. 
Notes: Macros are to be defined the same way in case they are used (see CA1 and CA2). 
Status: Rule


CL2. Declare each variable with the smallest possible scope and initialize it at the same time.
It's better to declare variables close to places where they are used at first. Otherwise, additional troubles may appear in finding out the type and purpose of a particular variable. It's also important to initialize the variable immediately (even if it has an integral type), so that its value is well defined. 
Example:
- (CGFloat)position
{
   // Wrong: The variable is not initially initialized and declared
   // not close the place of its first use.
   NSInteger theIndex;
   //...
   CGFloat thePosition = 0.0;
   for (;; ++theIndex)
   {
      // Right: Correct declaration
      CGFloat theOffset = 0.0;
      //...
      thePosition = theOffset * theIndex;
   }
   return thePosition;
}
Status: Rule


CL3. Do not use the same variable name in outer and inner scope.
If the same variable name is used in outer and inner scope the code would be very hard to understand; and it would certainly be a major error prone condition. 
Example: 
- (void)foo
{
   NSInteger theCounter = 0; 
   //...
   for (;;)
   {
      // Right: Correct declaration
      NSInteger theLocalCounter = 0;
      // Wrong: The same name of variable is used: the variable in outer scope
      // is hidden
      NSInteger theCounter = 0;
   }
}

Status: Rule


CL4. Declare each variable in a separate declaration statement.
Declaration of multiple variables on the same line is difficult to read and understand and provokes common mistakes. 
Example: 
// Right: The following declaration looks much better
NSInteger theIndex = 0;
NSInteger *theIndexPointer = NULL;
NSInteger theArray[100];
NSInteger (*HPLFunctionPointer)();
// Wrong: The declarations look definitely unclear
NSInteger theIndex = 0, *theIndexPointer = NULL, theArray[100], (*HPLFunctionPointer)();

Status: Rule


CL5. Avoid initialization of instance variables to zero.
Instance variables are initialized to zero automatically. It is not needed to add explicit initializations to nil or NO in the init method. 
Status: Recommendation

Conversions
CC1. Use explicit rather than implicit type conversion.
Most conversions are bad in some way. They can make the code less portable, less robust, and less readable. It is therefore important to use only explicit conversions. Implicit conversions are considered almost always bad. 
Example: 
- (NSInteger)defaultTemperature
{
   // Wrong: Implicit conversion of floating point number to integer
   return 36.6f;
}

Status: Recommendation

Object-Oriented Programming
CB1. Declare member variables private.
This ensures that data members are only accessed from within member functions. Hiding data (encapsulation) makes it easier to change implementation and provides a uniform interface to the object. Usage of protected ivars is not recommended, use corresponding accessors instead. 
Example: 
@interface HPLFoo : NSObject 
   @private 
       NSInteger index; 

//... 

@end

Status: Rule

Error Handling and Exception Safety
Exception safety means a correct work of functions/methods in the presence of exceptions. Never make exception safety afterthought, since it affects a class' design. Exception-unsafe and poor design go hand in hand almost always. 
TBA

Parts of C/C++ to Be Avoided
CA1. Do not use preprocessor macros, except for system provided ones.
Use functions rather than the preprocessor macros. 
Example: 
// Right
- (NSInteger)abs:(NSInteger)aX
{
   return (aX >= 0 ? aX : -aX);
   {color:#38761d}} 

// Wrong 
#define ABS(aX) ((aX >= 0) ? (aX) : (-(aX)))

Status: Rule


CA2. Do not use macros to define symbolic constants or enumerations.
Example: 
// Right
const int kHPLMaxValue = 999;

// Wrong
#define HPL_MAX_VALUE 999

Exceptions: 
  • Floating point constants to be defined with maximal precision on different platforms should defined using macros, for example: M_PI, M_LN2, etc.
  • Macros should be used to define constants in order to decrease a number of exported symbols in libraries or frameworks
  • In addition, Objective-C strings used as keys for key-value coding (KVC) should be defined using macros to show their real values; these values are supposed to be used directly and to form key paths at place 
Notes: 
Actual values of constants defined as macros for libraries or frameworks cannot be changed later (since they are already compiled into the client code) while for exported ones this could be done. 
Status: Rule


CA3. Do not use union types.
Unions can be an indication of a non-object-oriented design that is hard to extend. The usual alternative to unions is inheritance and dynamic binding. The advantage of having a derived class representing each type of value stored is that the set of derived class can be extended without rewriting any code. Because code with unions is only slightly more efficient, but much more difficult to maintain, it is to be avoided. 
Status: Rule

Readability and Maintainability
CR1. Avoid duplicated code and data.
This statement has two meaning: 
The first, and most evident, is that one must avoid simply cutting and pasting pieces of code. When similar functionalities are necessary in different places, those should be collected in class methods, and reused. 
The second meaning is at the design level, and is the concept of code reuse. Reuse of code has the benefit of making a program easier to understand and to maintain. An additional benefit is better quality because code that is reused gets tested much better. 
Status: Recommendation


CR2. Optimize code if and only if a performance problem is found.
Don't make assumptions regarding the optimization beforehand. This means that during the implementation phase the code that is easy to read, understand and maintain is to be written. Do not write cryptic code, just to improve its performance. 
Performance problems are more likely solved at an architecture and design level. 

Status: Recommendation