
In a nutshell: how Cocoa and Objective-C code should look
Once you've figured out the basics of Objective-C and Cocoa, you're ready to start writing
some code. But the conventions of naming and formatting Objective-C code are not the same
as traditional C, C++ or even Java programs.
Apple has laid out a set of guidelines as to how code should be written for maximum
clarity and integration with the frameworks. In addition, there are some undocumented
conventions that Cocoa developers often use.
In essence, the human factor present in Apple's products for the end user
can be found in Objective-C and Cocoa themselves.
1 of 8
Method Names: Returning Objects
In addition to simple accessors, classes or objects can also return
objects based on various conditions or input. The format is simple:
[object/class thing+condition];
[object/class thing+input:input];
[object/class thing+identifer:input];
Examples
realPath = [path stringByExpandingTildeInPath];
fullString = [string stringByAppendingString:@"Extra Text"];
object = [array objectAtIndex:3];
// class methods
newString = [NSString stringWithFormat:@"%f",1.5];
newArray = [NSArray arrayWithObject:newString];
If I wrote some of my own, this is what they might look like.
recipients = [email recipientsSortedByLastName];
newEmail = [CDCEmail emailWithSubjectLine:@"Extra Text"];
emails = [mailbox messagesReceivedAfterDate:yesterdayDate];
Note that all of these messages first indicate what kind of thing will be returned,
followed by what the circumstances will be for returning it. Also note that the last word
right before the colon describes the type of the supplied argument.
Sometimes, you want a variation on a value. In that case, the format is generally:
[object adjective+thing];
[object adjective+thing+condition];
[object adjective+thing+input:input];
Examples
capitalized = [name capitalizedString];
rate = [number floatValue];
newString = [string decomposedStringWithCanonicalMapping];
subarray = [array subarrayWithRange:segment];
2 of 8
Avoid Ambiguity
Bugs are scarce where code doesn't leave questions unanswered.
Ambiguous Messages
-sortInfo
-refreshTimer
-update
-fetchInfo:
On the surface, these methods may seem good. For example, they all have vowels. But
what they share is the potential for different interpretations because of the language used.
- sortInfo - returns sort info, or sort something called "info"?
- refreshTimer - return a timer used for refreshing, or refresh a timer?
- update - a verb? what is updated and how?
- fetchInfo - fetch info about something or give info about a fetch?
This is all fixed by slightly modifying the phrases:
Clear Messages
-currentSortInfo // "current" obviously describes the noun "sort info"
-refreshDefaultTimer // refresh is now clearly a verb
-updateMenuItemTitle // an action is taking place
-infoForFetch: // now we know info is returned for a fetch
By just adding a few characters to each method, we've removed almost all ambiguity. And there was
much rejoicing.
3 of 8
Global C Functions
Global C functions generally use one of a few simple formulas:
Prefix + Value ()
Prefix + Value + With/From/For + Input ()
Prefix + Action ()
Prefix + Action + Type ()
Just like classes, functions are prefixed with initials in order to prevent namespace issues.
Since C functions are free-standing by nature, the function name needs to indicate data types
they interact with, if any.
Functions from Cocoa
NSHomeDirectory()
NSHomeDirectoryForUser()
NSClassFromString()
NSBeginAlertSheet()
NSDrawGrayBezel()
Core Services Functions
In Core Services frameworks such as Core Foundation and Core Graphics, functions
are prefixed with the most-relevant opaque type, and the
"get" prefix is used even when there's no indirection involved:
Core Foundation
CFArrayGetValueAtIndex() // returned directly, but "get" is still used
CFDictionaryGetValue()
CFDateGetAbsoluteTime()
Core Graphics / Quartz
CGImageCreate()
CGImageGetHeight()
CGImageGetTypeID()
4 of 8
Other Global Symbols
There are several types of symbols with global scope in addition to C functions. For example:
- Constants
- Typedef'd structs
- Typedef'd enums
- Individual enum values
- Objective-C Protocols
Protocols, constants, structs, enums and enum values are basically named according to the same rules as classes or C functions. Capitalized with
initials at the front:
Structs
NSPoint point; // struct
NSRange range; // struct
NSRectArray * rects; // c-style array of structs
In Cocoa, enums are frequently used as "modes" for methods:
range = [string rangeOfString:@"find me" options:NSLiteralSearch];
Both constants and enums have a suffix that indicates what kind of thing they are:
Constants and Enums
// search modes (enums)
NSLiteralSearch
NSCaseInsensitiveSearch
// exception names (constants)
NSMallocException
NSInvalidArgumentException
// notification names (constants)
NSTaskDidTerminateNotification
NSWindowWillMoveNotification
Also, note that notification names have a slightly different formula:
Class of Affected Object + Did/Will + Action + "Notification"
5 of 8
Dynamic Typing
Objective-C is a dynamically-typed language, meaning that you don't have to tell the
compiler what type of object you're working with at compile time.
Declaring a type for a varible is merely a promise which can be broken at runtime
if the code leaves room for such a thing. You can declare your variables
as type id, which is suitable for any Objective-C object.
Dynamically-Typed Variables
id hostName;
id ipAddress;
id keyedAccountNames;
id theObject;
// the compiler is fine with this
theObject = [NSString string];
theObject = [NSNumber numberWithInt:1];
theObject = [NSDictionary dictionary];
So why specify type at all? Three basic reasons:
- To be clear: Makes it clear what you intend to do with
the variable
- To avoid useless warnings: If multiple classes share the same
method name, the compiler may warn you when you send that message to a generic object.
- To get useful warnings: If you mispell a message name, such as "-stringg", the compiler will tell you that NSMutableString does not implement such a method
In addition, there are situations where the id type makes the most sense. Some are:
- A delegate or datasource
- Object for a notification
- Contents for a generic container
- Objects involved in target/action
So no hard rules here, but a good basic practice is to specify the type if you have reason
to believe other types of objects wouldn't make sense in a given situation.
6 of 8
When to Use Accessors
Short answer: always
Why not just directly access instance variables within a class? It's a lot less code, right?
The reason is flexibility. If your code gets to data via accessors, it's easy to change
how that data is manipulated later.
For example, it gives you a good entry point for registering the previous value
with NSUndoManager. Or when debugging, it might help to log when a particular
instance variable is fetched.
But there's an even better reason to create accessors: Key-Value Coding. KVC is a protocol
used throughout Cocoa, most notably in bindings. It will look for accessors for your keys
first, and only access data directly as a last resort. You can shorten KVC's search and thus
speedup data access by implementing accessors.
Also be aware that if you set instance variables directly, in the form of var = value, Key-Value
Observing will not notice the change and bound objects will not get the new value.
7 of 8
Naming Parameters
Let's take a quick visit to method parameters. What's the standard here? The guidelines
are considerably more loose, but typically you prefix the input name with "the",
"an" or "new"
Prefixes for Method Parameters
- (void) setTitle: (NSString *) aTitle;
- (void) setName: (NSString *) newName;
- (id) keyForOption: (CDCOption *) anOption
- (NSArray *) emailsForMailbox: (CDCMailbox *) theMailbox;
- (CDCEmail *) emailForRecipients: (NSArray *) theRecipients;
Also worth noting is the naming convention for a loop. Typically, the current
object in the loop is prefixed with "one" or "a/an". Some also
simply refer to the individual object as "item":
Variable Names in Loops
for (i = 0; i < count; i++) {
oneObject = [allObjects objectAtIndex: i];
NSLog (@"oneObject: %@", oneObject);
}
NSEnumerator *e = [allObjects objectEnumerator];
id item;
while (item = [e nextObject])
NSLog (@"item: %@", item);
8 of 8
Odds and Ends
If you're sending a message with a particularly long name, break it up into multiple
lines:
Splitting Up Long Message Names
color = [NSColor colorWithCalibratedHue: 0.10
saturation: 0.82
brightness: 0.89
alpha: 1.00];
Classes that return one and only one instance of themselves (singletons) are named and implemented as follows:
Singleton
@implementation CDCMailboxManager
+ (id) sharedMailboxManager
{
static CDCMailboxManager * shared = nil;
if ( !shared )
shared = [[self alloc] init];
return shared;
}
Wrap Up
Assuming you've already read Part I, you should be fully-equipped to
go out and start writing code that looks and feels like Cocoa itself. If you hunger for even more
style, be sure to check out Apple's coding guidelines in the links list below.
As always, let us know what you think about the tutorial.
Further Reading