| < Previous PageNext Page > |
Library functions impose few restrictions on the programs that use them; you can call them whenever you need to. The methods in an object-oriented library or framework, on the other hand, are tied to class definitions and can’t be invoked unless you create or obtain an object that has access to those definitions. Moreover, in most programs the object must be connected to at least one other object so that it can operate in the program network. A class defines a program component; to access its services, you need to craft it into the structure of your application.
That being said, some framework classes generate instances that behave pretty much as a set of library functions. You simply create an instance, initialize it, and either send it a message to accomplish a task, or you insert it into a waiting slot in your application. For example, you can use the NSFileManager class to perform various file-system operations, such as moving, copying, and deleting files. If you need to display an alert dialog, you would create an instance of the NSAlert class and send it the appropriate message.
In general, however, environments such as Cocoa are more than a grab bag of individual classes that offer their services. They consist of object-oriented frameworks, collections of classes that structure a problem space and present an integrated solution to it. Instead of providing discrete services that you can use as needed (as with function libraries), a framework maps out and implements an entire program structure—or model—that your own code must adapt to. Because this program model is generic, you can specialize it to meet the requirements of your particular program. Rather than design a program that you plug library functions into, you plug your own code into the design provided by the framework.
To use a framework, you must accept the program model it defines and employ and customize as many of its classes as necessary to mold your particular program to that model. The classes are mutually dependent and come as a group, not individually. At first glance, the need to adapt your code to a framework’s program model might seem restrictive. But the reality is quite the opposite. A framework offers you many ways in which you can alter and extend its generic behavior. It simply requires you to accept that all Cocoa programs behave in the same fundamental ways because they are all based on the same program model.
Kinds of Framework Classes
Cocoa API Conventions
The classes in a Cocoa framework deliver their services in four ways:
Off the shelf. Some classes define off-the-shelf objects, ready to be used. You simply create instances of the class and initialize them as needed. Subclasses of NSControl, such as NSTextField, NSButton, and NSTableView (along with their associated NSCell classes), fall into this category. You typically create and initialize off-the-shelf objects using Interface Builder, although you can create and initialize them programmatically.
Behind the scenes. As a program runs, Cocoa creates some framework objects for it “behind the scenes.” You don’t need to explicitly allocate and initialize these objects; it’s done for you. Often the classes are private, but they are necessary to implement the desired behavior.
Generic. Some framework classes are generic, or abstract. A framework might provide some concrete subclasses of the generic class that you can use unchanged. Yet you can—and must in some circumstances—define your own subclasses and override the implementations of certain methods. NSView, NSDocument, and NSFormatter are examples of this kind of class.
Delegator and notifier. Many framework objects keep other objects informed of their actions and even delegate certain responsibilities to those other objects. The mechanisms for delivering this information are delegation and notification. A delegating object publishes an interface known as an informal protocol. Client objects must first register as delegates and then implement one or more methods of this interface. A notifying object publishes the list of notifications it broadcasts, and any client is free to observe one or more of them. Some of the delegator classes are NSApplication, NSText, and NSWindow. Many framework classes broadcast notifications.
Some classes provide more than one of these general kinds of services. For example, you can drag a ready-made NSWindow object from an Interface Builder palette and use it with only minor initializations. Thus the NSWindow class provides off-the-shelf instances. But an NSWindow object also sends messages to its delegate and posts a variety of notifications. You can even subclass NSWindow if, for example, you want to have round windows.
It is the Cocoa classes in the last two categories—generic and delegator/notifier—that offer the most possibilities for integrating your program-specific code into the structure provided by the frameworks. “Inheriting From a Cocoa Class” discusses in general terms how you create subclasses of framework classes, particularly generic classes. See “Communicating With Objects” for information on delegation, notification, and other mechanisms for communication between objects in a program network.
When you start using the classes, methods, and other API of the Cocoa frameworks, you should be aware of a few conventions that are intended to ensure efficiency and consistency in usage.
Methods that return objects typically return nil to indicate “failure to create” or “no object to return”. They do not return a status code.
The convention of returning nil is often used to indicate a runtime error or other non-exceptional condition. The Cocoa frameworks deal with errors such as “array index out of bounds” or “method selector not recognized” by raising an exception (which is handled by a top-level handler) and, if the method signature so requires, returning nil.
Some of these same methods that might return nil include a final parameter for returning error information by reference.
This final parameter takes a pointer to an NSError object; upon return from a method call that fails (that is, returns nil), you can inspect the returned error object to determine the cause of the error or you can display the error to the user in a dialog.
As an example, here’s a method from the NSDocument class:
- (id)initWithType:(NSString *)typeName error:(NSError **)outError; |
In a similar fashion, methods that perform some system operation (such as reading or writing a file) often return a Boolean value to indicate success or failure.
These methods might also include a pointer to an NSError object as a final by-reference parameter. For example, there’s this method from the NSData class:
- (BOOL)writeToFile:(NSString *)path options:(unsigned)writeOptionsMask error:(NSError **)errorPtr; |
Empty container objects are used to indicate a default value or no value—nil is usually not a valid object argument.
Many objects encapsulate values or collections of objects, for example, instances of NSString, NSDate, NSArray, and NSDictionary. Methods that take these objects as parameters may accept an “empty” object (for example, @"") to indicate “no value” or “default value”. For example, the following message sets the represented filename for a window to “no value” by specifying an empty string:
[aWindow setRepresentedFilename:@""]; |
Note: The Objective-C construct @“characters” creates an NSString object containing the literal characters. Thus, @““ would create a string object with no characters—or, an empty string. For more information, see String Programming Guide for Cocoa.
The Cocoa frameworks expect that global string constants rather than string literals are used for dictionary keys, notification and exception names, and some method parameters that take strings.
You should always prefer string constants over string literals when you have a choice. By using string constants, you enlist the help of the compiler to check your spelling and thus avoid runtime errors.
The Cocoa frameworks use types consistently, giving higher impedance matching across their API sets.
For example, the frameworks use float for coordinate values, CGFloat for both graphical and coordinate values, NSPoint (consisting of two CGFloat values) for a location in a coordinate system, NSString objects for string values, NSRange for ranges (start and offset), and NSInteger and NSUInteger for, respectively, signed and unsigned integral values. When you design your own APIs, you should strive for similar type consistency.
A substantial subset of Cocoa API conventions concerns the naming of classes, methods, functions, constants, and other symbols. You should be aware of these conventions when you begin designing your own programmatic interfaces. Some of the more important of these naming conventions are the following:
Use prefixes for class names and for symbols associated with the class, such as functions and typedef’s.
A prefix protects against collisions and helps to differentiate functional areas. The prefix convention is two or three unique uppercase letters, for example, the “AC” in ACCircle.
With API names, it’s better to be clear than brief.
For example, it’s easy to understand what removeObjectAtIndex: does but remove: is ambiguous.
Avoid ambiguous names.
For example, displayName is ambiguous because it’s unclear whether it displays the name or returns the display name.
Use verbs in the names of methods or functions that represent actions.
If a method returns an attribute or computed value, the name of the method is the name of the attribute.
These methods are known as “getter” accessor methods. For example, if the attribute is background color, the getter method should be named backgroundColor. Getter methods that return a Boolean value are of a slight variation, using an “is” or “has” prefix—for example, hasColor.
If a method sets the value of an attribute—that is, a “setter” accessor method—it begins with “set” followed by the attribute name.
The first letter of the attribute name is in uppercase—for example, setBackgroundColor:.
Note: The implementation of setter and getter methods are discussed in detail in “Accessor Methods” and in Model Object Implementation Guide.
Do not abbreviate parts of API names unless the abbreviation is well known (for example, HTML or TIFF).
For the complete set of naming guidelines for Objective-C programmatic interfaces, see Coding Guidelines for Cocoa.
A general, overarching API convention regards object ownership. Briefly stated, the convention is that a client owns an object if it creates the object (by allocation then initialization), copies it, or retains it (by sending it retain). An owner of an object is responsible for its disposal by sending release or autorelease to the object when it no longer needs it. For more on this subject, see “The Life Cycle of a Cocoa Object” and Memory Management Programming Guide for Cocoa.
| < Previous PageNext Page > |
© 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-12-20)
|