ios App Development for Everyone Kevin McNeish Table of Contents Chapter 2 Objective C (Part 6) Referencing Classes Now you re ready to use the Calculator class in the App. Up to this point, each time you have created an object instance from a class, you stored the newly created object in a local variable a variable you declared in a method. If you were to create a local variable to hold a reference to the calculator object, it would look like Figure 2-51. Figure 2-51: A local variable holding a reference to the Calculator object This works great as long as you only need to use the calculator object in one method of the view controller. If you need to access the Calculator object from multiple methods in the ViewController class, you can create a class-level instance variable from which the Calculator can be referenced (Figure 2-52).
Figure 2-52: A class-level instance variable holding a reference to the Calculator object As mentioned earlier, it s a common naming convention to include an underscore ( _ ) in an instance variable name as shown in Figure 2-52, and you will learn why in just a bit. The main thing to notice is the calculator object gets created in the viewdidload method, but it can also be accessed from the performcalculation method because it is stored in a class-level instance variable. Follow these steps to create the calculator_ instance variable to reference the Calculator object. 1. Go to the Project Navigator in Xcode and click on the ViewController.m file. Next, click to the immediate right of the following line of code: @implementation ViewController 2. Next, press Enter to add a new, empty line, and then add the following line of code, which declares an instance variable, whose type is Calculator (upper case C ) and name is calculator_ (lower case c ): @implementation ViewController Calculator *calculator_; @synthesize lbldemo; Adding an underscore ( _ ) to an instance variable name is a common convention in Objective-C. When you learn more about properties later in this chapter you ll find out why! 3. A few seconds after typing this line of code, an error icon appears. If you click on the error icon, you see the message Unknown type name Calculator :
Why are you getting this error? Xcode is telling you it doesn t know about the type, or class Calculator. Why not? The Calculator class definition files are included in the project as shown in Figure 2-53 where the files are highlighted in a red box (added for dramatic effect). Figure 2-53: Simply including class files isn t enough when referencing another class In Objective-C, it s not enough to simply have class definition files listed in the same project. Whenever you reference another class, you must include a declaration that provides information about that class. There are two ways you can do this. The first is to use the @class directive, and the second is to import the class header file. Each of these options provides a different level of detail about the class you are referencing and are appropriate under different circumstances. Let s take a side trip to thoroughly understand how to reference classes in your App before we get down to the business of testing the Calculator class. Using the @class Directive The @class directive provides minimal information about a class. In fact, the only thing it indicates is that the class you are referencing is a class! In Objective-C, the use of the @class directive is known as a forward declaration. Follow these steps to see how it works: 1. Add a new, empty line below the #import statement near the top of the ViewController.m file then add the following statement: #import "ViewController.h" @class Calculator;
As soon as you add the @class directive, the error icon disappears and the type of the variable, Calculator, becomes color-coded. This is because you have told Xcode that Calculator is a class by using the @class directive. Again, this is bare minimum information. All Xcode knows at this point is that Calculator is a class. It doesn t know any of its properties or methods. 2. Now let s create an instance of the Calculator class. This is the first time you have created an object from a custom class. It works exactly the same as creating an object from a Cocoa Touch class. You call alloc and init and it returns a new, initialized calculator object. To do this, in the ViewController.m file near the top of the viewdidload method, add a new, empty line below the call to [super viewdidload], and then add the following code that creates a new instance of the Calculator class: - (void)viewdidload { [super viewdidload]; calculator_ = [[Calculator alloc] init]; Again, you can see Xcode displays an error for this line of code. If you click the error icon, you see the message Receiver Calculator for class message is a forward declaration : What does this mean? It means Xcode recognizes Calculator as a class because of the @class Calculator directive, but it needs more information to create an instance of the class. You would also get an error if you tried to send a message to an instance of the Calculator class because again, Xcode doesn t have enough information about the class to know which methods it implements. This is where you need to import a class header. Importing a Class Header The second way to provide information about a class is to import its header file. Remember, the header file contains important information about the properties, methods and heritage of a class, so it provides much more information than the simple @class directive. Importing the header file for the Calculator class gives the ViewController class enough information to create a variable of the type Calculator, and create an instance of the Calculator. To import the class header:
1. Go to the top of the ViewController.m file, remove the @class directive and replace it with the following #import directive: #import "ViewController.h" #import "Calculator.h" As soon as you do this, the error associated with creating an instance of the Calculator class disappears! 2. Now take a look at the #import "ViewController.h" directive listed directly above the #import directive you just added. This import statement is automatically added when you create a new class in Xcode. Why is this needed? Because not only does a class need to import the header file of any class it uses, it must also import its own header file. 3. To see another example of importing a header file, select the Calculator.h header file in the Project Navigator. Notice it imports the Foundation.h header file. This import statement is also automatically added when you create a class: #import <Foundation/Foundation.h> @interface Calculator : NSObject Why does this header file need to be imported? Because Calculator is a subclass of NSObject, and the NSObject class header file is found in the Foundation.h header file. Why isn t this header file called NSObject.h? Unlike the header files you have seen so far, this and other Cocoa Touch header files contain a reference to the header files of many classes. This makes your life easier, so you only have to import one super header file rather than one header file for each class you use in a particular Cocoa Touch framework. Notice you use angle brackets ( < > ) to import a header file for a class that is part of the Cocoa Touch Framework as in this example. In contrast, you use double quotes ( ) to import a header file that is defined in your project. Importing Superclass Header Files Whenever you specify a superclass in a class header file, you need to import the header file of the superclass. In the following example, BusinessObject is the superclass of Customer, so the Customer class must import the BusinessObject.h header file: #import <Foundation/Foundation.h> #import "BusinessObject.h" @interface Customer : BusinessObject
This is important to know when you create your own custom classes that are not a subclass of a foundation class such as NSObject. Import in the Header or Implementation File? If you have been following closely in this section, you may have noticed the #import statement can be used in either the header (.h) file or the implementation (.m) file. So where should you use it? Here s a good rule of thumb: When you specify a superclass, import the superclass header file in the subclass s header file When you are referencing a class from within your implementation file, import the class header in your class implementation file When to use @class vs. #import So, you may be asking yourself, If #import does everything @class does and more, why not just use #import all the time? The answer is found in Apple s own documentation: The @class directive minimizes the amount of code seen by the compiler and linker, and is, therefore, the simplest way to give a forward declaration of a class name. Being simple, it avoids potential problems that may come with importing files that import still other files. So, use the @class directive when you can, and use #import when you must. Understanding Prefix Header Files When you create a new project in Xcode, a prefix header file is automatically added to your project. When you build your project, the compiler automatically adds the content of the prefix header file to every source code file in your project. This is a powerful tool that makes it easy to add import statements, in one place, so you don t have to add them manually to each and every source code file in your project. To see this file in the sample project, in the Project Navigator, click the grey triangle to the left of the Supporting Files folder to expand it, and then, select the ObjectiveCDemo-Prefix.pch file. You should see the following code:
As you can see, there are a few import statements listed here. The two statements at the bottom import header files for classes commonly used in ios Apps. The first, UIKit, references classes used to create your App s user interface. The second, Foundation, imports commonly used core classes including NSObject. So, here s a burning question. If: a) The import statements in the prefix header file are automatically added to the top of each source code file, and b) The prefix header file imports the Foundation.h file Why do you need the Foundation.h file automatically imported at the top of each new class you create? For example: #import <Foundation/Foundation.h> #import "BusinessObject.h" @interface Customer : BusinessObject @end The answer is, you don t. It is belt and suspenders code. That said, it doesn t hurt to have it imported, but it s not necessary since the import statement is already included in the prefix header file. Summary Here are some important points to remember when referencing classes: If you only need to reference an object from a single method, you can store a reference to the object in a local variable. If you need tor reference the object from multiple methods, you can
store it in a class-level instance variable In Objective-C, it s not enough to have class definition files listed in the same project. When you reference another class, you must include an @class or #import directive that provides information about that class The @class directive provides minimal information about a class. In fact, the only thing it indicates is that the class you are referencing is a class! In Objective-C, the use of the @class directive is known as a forward declaration. For example: @class Calculator; Another way to provide information about a class is to import its header file. The header file contains important information about the properties, methods and heritage of a class, so it provides much more information than the simple @class directive You use angle brackets ( < > ) to import a header file for a class that is part of the Cocoa Touch Framework as in this example. You use double quotes ( ) to import a header file that is defined in your project. For example: #import <Foundation/Foundation.h> #import "BusinessObject.h" Importing a class header file allows you to create a variable of that class type, or create an instance of that class Classes must import its own header file Classes must import their superclass header file When you specify a superclass, import the superclass header file in the subclass s header file. When you are referencing a class from within your implementation file, import the class header in your class implementation file Use the @class directive when you can, and use #import when you must Prefix header files are a powerful tool that makes it easy to add import statements, in one place, so you don t have to add them manually to each and every source code file in your project
Exercise 2-3 1. Create a new class called BusinessObject based on NSObject. 2. Change the Calculator class so it is based on the new BusinessObject class. The inheritance relationship is visualized as shown in Figure 2-54. Figure 2-54: Calculator class inheritance You will need your newfound class referencing skills to make this happen! Table of Contents