Best Practices
Apple Coding Guidelines
Readability
Write code for people to read, not the compiler.
ARC
Use ARC (Automatic Reference Counting) if at all possible.
ARC is supported in iOS 4.0+ with the single exception that weak reference nilling only available in iOS 5.0+.
Designated initializer
Every class needs a designated initializer and the method should start with “init”. Every other initializer must call the designated initializer.
Indenting
In Xcode use control-i for re-indent
Refactoring
In Xcode use “Edit in Scope” (right-click on a variable, select the down-arrow to the right) to change all instances of a variable at the same time.
Simplest
[NSMutableArray array]
instead of
[[NSMutableArray arrayWithCapacity:value]
Because it is clear and generally just as fast
Comments
Don’t add comments that duplicate the method call naming. If the method call naming needs a comment refactor the method name.
Method names
// Compose URL
NSURL *url = [NSURL URLWithString:urlString];
// Init validator with url
StoreVerifier *verifier = [[StoreVerifier alloc] initWithUrl:url store:storeCopy];
// Begin address validation
serverType = [verifier validateAddressWithAsyncHttp];
Method names should be verbs (action)
// Update UI.
[self validationHasBegun];
beter
[self updateGUIForValidationHasBegun];
Tangelo-Mac-mini:Cocoa2HTML tangelo$
Description
Create class “description” methods.
Constructors
Favor full constructors to multiple setters. The designated initializer should have enough arguments to fully create a usable instance without needing to use setters. If there are “to many” arguments the class is probably trying to do to much and should be broken up into multiple classes.
NSAssert
Use NSAssert(NO, @”message”); for programmer errors.
setters/getters
Name setters/getters from the client perspective
Refactor
Get code working then refactor structure
Collections
If possible instead of providing a collection getter provide an enumerator to a caller.
case
Instead of a case (or if/else/…) that selects code consider subclassing. Using case to select data if fine.
NSDictionary
Do not use dictionaries as substitutes for classes.
Comments
Delete all commented out code (you can always recover it from the source control system).
Boy Scout Principal
When editing any file leave it in better condition that you found it.
Constant Strings
There is no size saving with stringWithString but there may be a slight performance degradation. The @”string” is still a compiled-in constant.
NSString *desc = [NSString stringWithString:@"Text"];
vs
NSString *desc =@”Text”;
NSString* urlString = [NSString stringWithString:@"http://"];
urlString = [urlString stringByAppendingString:address];
vs
urlString = [@"http://" stringByAppendingString:address];
or
urlString = [NSString stringWithFormat:@"http://%@", address];
Retain (or Strong) vs Copy
Copy because the property may be set with a mutable string and copying will reduce the possibility mutating the string.
@property(nonatomic, retain) NSString *address;
vs
@property(nonatomic, copy) NSString * address;
There is no cost of copying an immutable string.
Brent Simmons says no
Bill Bumgarner says yes, especially for NSString
Public vs Private
Only put methods, properties and ivars that are part of the public interface By Design in the interface file (.h).
Put private methods in a class extension in the implementation file (.m).
Put private @properties in a class extension in the implementation file (.m).
Put private ivars in the @implementation of the implementation file (.m). llvm 4.0 and above
Properties
In Xcode 4 and above it is not necessary to declare ivars for properties
In llvm 4 and above @synthesize is not necessary nor is declaring an ivar and defaults to creating an ivar for the property with an leading underscore.
Source Control Commit
Always compare prior to commit and make the commit comment match the changes.
get…
Use the “get” prefix only for methods returning results via reference in the parameter list and ake the reference var(s) last.
- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path error:(NSError **)error {}
In Objective-C, pass by reference smells like you are doing it wrong with very few exceptions.
Output arguments are counterintuitive, have it change the state of the object it is called on if necessary.
NSError is an exception.
- (void)getCharacters:(unichar *)buffer {}
Dot notation
Do not use dot for behavior.
Use dot to access or set attribute like things, typically attributes declared as properties.
These are OK even though they are not properties because they smell like properties:
k = anArray.count;
for (NSView *v in myView.subviews) { … };
Method Names
Method names should have the last word prior to the argument specify the argument.
Good: – (void)carsWithColor:(UIColor *)color wheels:(NSInteger)wheels;
Bad: – (void)cars:(UIColor *)color wheels:(NSInteger)wheels;
Method names should start with a lower case letter
Class names start with an upper case letter
Simple variants
[NSMutableArray array];
instead of:
[NSMutableArray arrayWithCapacity:value];
It is clearer, easier and probably just as fast
Less code means fewer errors and less thought taken away from more important aspects for the code.
NARC
N – new
A – alloc
R – retain
C – copy
Methods with names containing NARC return a retained object.
Methods with names not containing NARC return an autoreleased object.
Core foundation methods named Create… return retained objects and CFRelease() must be called.
Dealloc
Put the dealloc method below the last init method
It is as important as init…
OrNil
If a method parameter can be either a value or nil add OrNil to the name:
+ (UINib *)nibWithData:(NSData *)data bundle:(NSBundle *)bundleOrNil;
NSNotification
Only use when the sender does not know the receiver and/or there are multiple receivers.
Use the Apple recommended naming convention:
[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification
Examples:
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification
Commented-Out Code
When you see commented-out code, delete it! Don’t worry, the source code control system still remembers it.
DRY principle
“Don’t Repeat Yourself”
Duplicated code means duplicate changes when updating code and the likely chance not all instances will be updated.
Every time you see duplication in the code, it represents a missed opportunity for abstraction.
Vertical Separation
Variables and function should be defined close to where they are used.
Private functions should be defined just below their first usage.
Finding a private function should just be a matter of scanning downward from the first usage.
Use Explanatory Variables
Break the calculations up into intermediate values that are held in variables with meaningful names.
Method Names
Method names should say what they do.
If you have to look at the implementation (or documentation) of the function to know what it does.
Magic Numbers
Replace Magic Numbers with named constants.
In general it is a bad idea to have raw numbers in your code. You should hide them behind well-named constants.
Encapsulate Conditionals
Instead of:
if (timer.hasExpired() && !timer.isRecurrent()) {
// …
}
Create a method to encapsulate the conditional logic:
- (BOOL)shouldBeDeleted:(NSTimer *)timer {
return timer.hasExpired() && !timer.isRecurrent();
}
if (shouldBeDeleted(timer)) {
// …
}
Avoid Negative Conditionals
Negatives are just a bit harder to understand than positives.
Bad: if (!buffer.shouldNotCompact())
Good: if (buffer.shouldCompact()))
SRP
Single Responsibility Principle
Functions that do more than one thing should be converted into many smaller functions, each of which does one thing.
Constants versus Enums
Use enums rather than constants or #defines, enums are type checked.
Avoid Encodings
Names should not be encoded with type or scope information. Prefixes such as m_ or f are useless in today’s environments.
Polish notation is not a good fit for Cocoa.
Names Should Describe Side-Effects
Don’t hide side effects with a name.
Bad: getOos
Good: createOrReturnOos