It’s perfectly possible for the interface and implementation of a class to appear in the same file, or for multiple classes to be defined in a single file, but this is not the usual convention. The usual convention is one class, two files: one file containing the interface section, the other file containing the implementation section. For example, let’s suppose you are defining a class MyClass. Then you have two files, MyClass.h and MyClass.m. (The file naming is not magical or necessary; it’s just part of the convention. The file extensions are pretty much necessary, though, because the build process and Xcode itself rely on them.) The interface section goes into MyClass.h, which is called the header file. The implementation section goes into MyClass.m, which is called the implementation file. This separation into two files is not inconvenient, because Xcode, expecting you to follow this convention, makes it easy to jump from editing a .h file to the corresponding .m file and vice versa (Navigate → Jump to Next Counterpart; in Xcode 3.2.x, View → Switch to Header/Source File). Finally, the implementation file imports the header file (see Chapter 1 on the #import
directive); this effectively unites the full class definition, making the definition legal even though it is split between two files, and allowing the implementation section to “see” any method declarations in the interface section.
With this arrangement in place, further imports become easy to configure. The header file imports the basic header file for the entire Cocoa framework; in the case of an iOS program, that’s UIKit.h (again, see Chapter 1). There is no need for the implementation file to import UIKit.h, because the header file imports it, and the implementation file imports the header file. If a class needs to know about another class that isn’t already imported in this way, its implementation file imports that class’s header file. Example 4-1 summarizes this conventional schema.
Example 4-1. Conventional schema for defining a class
// [MyClass.h] #import <UIKit/UIKit.h> @interface MyClass : NSObject { // instance variable declarations go here } - (NSString*) sayGoodnightGracie; @end // [MyClass.m] #import "MyClass.h" #import "OtherClass.h" @implementation MyClass - (NSString*) sayGoodnightGracie { return @"Good night, Gracie!"; } @end
The result of this arrangement is that everything has the right visibility. No file ever imports an implementation file; that way, what’s inside a class’s implementation file is private to that class. If something about a class needs to be public, such as a method that you want other classes to be able to call, it is declared in the header file, and other classes import that header file in their implementation files (as I do with OtherClass.h in Example 4-1); this keeps the chain of imports clear and simple.
A header file is also an appropriate place to define constants. In Chapter 1, for example, I talked about the problem of mistyping the name of a notification or dictionary key, which is a literal NSString, and how you could solve this problem by defining a name for such a string:
#define MYKEY @"mykey"
The question then arises of where to put that definition. If only one class needs to know about it, the definition can go near the start of its implementation file (it doesn’t need to be inside the implementation section). But if multiple classes need to know about this name, then a header file is an appropriate location; every implementation file that imports this header file will acquire the definition, and you can use the name MYKEY
in that implementation file.
A slight problem arises when a header file needs to mention one of your other classes. Suppose, for example, that MyClass has a public method that takes or returns an instance of MyOtherClass, or that MyClass has an instance variable whose type is MyOtherClass. So MyClass.h needs to speak of MyOtherClass*
. But MyClass.h does not import MyOtherClass.h, so MyClass.h doesn’t know about MyOtherClass, and the compiler will complain. To silence the compiler without violating the arrangement of imports (by importing MyOtherClass.h in the header file MyClass.h), use the @class
directive. The word @class
is followed by a comma-separated list of class names, ending with a semicolon. So MyClass.h might start out like this:
#import <UIKit/UIKit.h> @class MyOtherClass;
Then the interface section would follow, as before. The @class
directive simply tells the compiler, “Don’t worry, MyOtherClass really is the name of a class.” That’s all the compiler needs to know in order to permit the mention of the type MyOtherClass*
in the header file.
If, on the other hand, MyClass is to be a subclass of some other class, then MyClass’s header file must import that superclass’s header file (or some other header file that imports that superclass’s header file). Thus, for example, in Example 4-1, MyClass.h imports UIKit.h; thus it knows about NSObject, so that MyClass can declare NSObject as its superclass.
A question that may occur to you at this point is how to declare a method without making it public. For example, let’s say that many methods in MyClass need to call myCoolMethod
, which is also a MyClass method. To make myCoolMethod
visible to all MyClass methods, regardless of the order in which they are defined in the implementation section, you can declare myCoolMethod
in the interface section. But this effectively “publishes” myCoolMethod
, because any other class that imports MyClass.h will now know about it. If that isn’t something you want to do, there’s a trick for creating a second interface section that only MyClass’s implementation section can see (Chapter 10).
The Global Namespace
When defining classes, choose your class names wisely to prevent name collisions. Objective-C has no namespaces; there’s a single vast namespace containing all names. You don’t want your own class name (or, for that matter, any other top-level constant name) to match a name defined in Cocoa. Instead of namespaces, there’s a convention: each Cocoa framework prefixes its names with a particular pair of capital letters (NSString and NSArray, CGFloat and CGRect, and so on). Apple suggests that you use a prefix of your own as well. Don’t use any of Apple’s prefixes. Nothing limits your prefix to two letters, or requires that both letters be uppercase. In fact, because all of Apple’s own prefixes are two uppercase letters, “My” as a prefix is safe.
Get Programming iOS 4 now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.