Hour 8. Declaring Instance Variables in an Interface File


What You’ll Learn in This Hour

Image Creating an instance variable for a class instance

Image Using id

Image Working with static typing

Image Using variable scoping


Declaring Instance Variables and Properties

There are two ways of declaring the data elements that are part of a class. You can declare them as instance variables in the same way that you declare integers and floats. You can also declare them as properties of the class. This hour explores the use of instance variables. In the next hour, Hour 9, “Declaring Properties in an Interface File,” you find out how to use properties to accomplish the same results. With declared properties, the compiler actually generates the declarations you see in this hour along with accessors that help to enforce the opacity of objects, which is a hallmark of good object-oriented design.

The use of declared properties is preferred today rather than using instance variables in your code. However, behind your declared properties are the instance variables that are automatically generated when your project is built. For that reason, instance variables are described in this hour and properties are dealt with in the following hour. Just remember that this is background information and that you should be using declared properties in your code. There are a few cases in which you do need to be aware of the instance variables and they are dealt with in these two hours as necessary.

Using the Class

You have created the class inside your project, but it is not instantiated, and there is no way that you can use it in your code until you instantiate it.


Tip

Yes, you can use class methods without instantiation, but for a bare-bones class such as the CurrencyConverter class you built in Hour 7, “Declaring a Class in an Interface File,” the only class methods are those that are inherited from NSObject.


By placing the class files in your project, you have given them a location and context in the world of the code, but now you need to consider where in the runtime world of your app this class would be used. (Of course, in practice this is a discussion that you should have right at the beginning when you are designing the class.)

Placing a Class Instance in Context

Some object in your app will need to instantiate one or more CurrencyConverter instances. Depending on your class and your point of view, you may well think that some object (or objects) must instantiate, own, and control the instance(s) of the class. That owner or controller must be in existence before the class is instantiated—a prime mover in Aristotle’s philosophy.

Eventually, this instance of your class may be passed on to other owners or controllers, but in the normal course of things, some object that survives the instance of your class needs to dispose of it. Today, that is usually done with Automatic Reference Counting (ARC), but although ARC manages the disposal of objects, you still are responsible for managing data. For example, ARC will dispose of a text field that is no longer needed, but it is your responsibility to move data entered into that field to a permanent storage location such as Core Data.


Note

This is a high-level conceptual look at instance life cycles. It is not a precise discussion of memory management.



Where Automatic Reference Counting (ARC) Fits In

Before ARC was implemented (in Xcode 4), the object life cycle was implemented in code. You created and initialized an object with this code:

Click here to view code image

myObject = [[MyObject alloc] init];

(Note that the previous line of code contains method calls.)

When you were finished with it, you deallocated it with this code:

[myObject dealloc];

With ARC, the compiler inserts the dealloc method call automatically; in fact, when you are using ARC, you are not allowed to use dealloc in your own code. The decision of when to deallocate an object is made by the compiler as part of its analysis of the structure of your code. ARC also manages ownership of objects with retain, release, and autorelease, which you also no longer need to use.

It is important to note that ARC was implemented in Xcode 4.2, but that it was an option for your project. There is a great deal of existing code that does not use ARC, so it is important to understand how those apps work. The Edit, Factor, Convert to Objective-C ARC command provides a tool to automatically convert a target to use ARC, so much legacy code has already been converted. With Xcode 5, ARC is no longer an option for new projects: It is on by default.


GO TO Image Hour 16, “Managing Memory and Runtime Objects,” p. 221, for more information about ARC.

Choosing the Context

Deciding on the owner or controller of an instance is simple in many cases. For a view instance, a descendant of NSViewController or UIViewController is the most likely candidate to manage the view instance. In a document-based app, it is often a document that brings together many of the class instances in the app—certainly some of the instances related to the document’s content. The CurrencyConverter class may be written in such a way that it could become part of a view or part of a document—or both.

Looking ahead to see what classes in your app might be users of a class you are considering developing is a good part of the design process. You can write a currency converter class that is designed to fit into a view, one that is designed to fit into a document, or one that can do both.


Note

The most significant difference might have to do with the degree of interactivity (if any) that the currency converter supports. A totally internal class might receive its data through arguments and return the results as a method result. Errors can be reported through the method result.


Returning Multiple Result Values from a Method

If you want to return multiple values from a method, you can use one of the collection classes. The most commonly used class for this purpose is NSDictionary.

GO TO Image Hour 15, “Organizing Data with Collections,” p. 205, for more information on returning multiple values from a method.

In other cases, the app itself is responsible for its class instances. (These non-document-based apps are often referred to as library or shoebox apps. Examples are Address Book and iCal.)

If the app is to own an instance of CurrencyController, it needs to have its own instance variable so that it can refer to the instance (and eventually dispose of it). It needs to create an instance of the class and assign it to its own instance variable.

Unlike other object-oriented languages, Objective-C does not extend its base classes in many cases. Certainly, UIApplication and NSApplication are not subclassed very often. Instead, they have app delegates, and it is those classes that you subclass.

So the next step is to add an instance variable for a class instance to the interface file, to create an instance of the class, and to set the variable to the newly created instance. The next sections of this hour show you three different ways of declaring instance variables to contain instances of classes and compare their functionality. Declarations of noninstance variables are traditional C syntax.

Creating an Instance Variable for CurrencyConverter with id

If you have implemented and imported a CurrencyConverter class, you can declare an instance variable using the type id, as shown in Figure 8.1.

Image

FIGURE 8.1 Declare an instance variable with id.

id is a basic data type in Objective-C. It can be set to an instance of any class. Because it is part of the Objective-C language, as you see in Figure 8.1, you do not need to import the header file for CurrencyConverter. Although id will be set to an instance of the class CurrencyConverter, in this declaration it is declared as a generic id.

The implementation file compiles cleanly and runs without the header file, as shown in Figure 8.2.

Image

FIGURE 8.2 Use the id instance variable in an implementation file.

Note that the only line of code that has been added to this file is line 18. All of the other code was created by Xcode as part of the template.

In Figure 8.2, two breakpoints have been set. You do this by clicking in the gutter on the line of code where you want to break. In this case, execution stops just before line 18, and again just after it. The reason for these breakpoints is so that you can inspect the object that has been created with the debugger.

What Happens When Execution Stops

When you run the app, execution stops at the first breakpoint (notice a small green arrow on the current line just to the right of the breakpoint itself). The debugger can be shown at the bottom of the workspace window with View, Debug Area, Show Debug Area or with the button at the top right of the workspace window. Within the debug area, you can use the buttons at the top right to display the console at the right, variables at the left of the debug area, or both. In Figure 8.2, only the variables are shown.

When execution has stopped, you might need to do a bit of rearranging to see the data shown in Figure 8.2. You almost certainly need to use the disclosure triangle next to self; you want to see the variables within the current object, which is an instance of AppDelegate as you see in the parentheses. (The asterisk is discussed shortly.) You also see the memory location for the object.

Within the object, you can see the _window instance variable that is declared at the top of the implementation file as part of the template. You also see the currencyConverter instance variable that you created in the header file shown in Figure 8.1. (NSObject is discussed in the following section.)

What is most important in Figure 8.2 is the memory location for currencyConverter. It is 0×0—no memory is allocated because the object has not been created. Execution has stopped just before the alloc line of code.

Continue to the next breakpoint with the Continue button (second from the left of the debugger controls) or use the Step button to go one step further (third from the left in the control buttons). As you can see in Figure 8.3, there is now a memory location assigned to currencyConverter; the object has been created. Also, instead of being identified as of type id as it was in Figure 8.2, it is now identified as being of type/class CurrencyConverter. (In this context, type and class are interchangeable.)

Image

FIGURE 8.3 Step one line further.

Dynamic Binding

id is the type of any class instance. As you see in the debugger, at runtime, the type of the instance that is assigned to an id variable is known. You can assign an id to another id, and you can pass them in and out of methods just as you would an integer or other typed variable. You are responsible for making certain that the actual types that are created and stored in an id variable are correct.

It is legal to send a message to id even if the underlying object cannot respond to it. (In fact, that is necessary.) If the object cannot respond to a message, it simply does not do so. This means that in the code shown here, you can send the display message to currencyConverter, which is of type id.

This code does not generate either compile or runtime errors:

[currencyConverter display];

This is an example of dynamic binding, one of the key features of Objective-C. This flexibility lets you write very sophisticated code that is put together with the specific objects involved at runtime.

It also enables you to create problems for yourself and others who modify your code. For cases in which you are not certain what you are dealing with, you can call class to get an object’s class and you can check to see if it responds to specific selectors (method calls).

GO TO Image There is more on method calls at “Sending a Message to the Text Field” in Hour 6, “Exploring Messaging and a Testbed App,” p. 92.

Creating an Instance Variable for CurrencyConverter with the Class Name

You can avoid these problems by using static typing. Instead of declaring the instance variable as type id, you can declare it specifically as type CurrencyConverter—the class that you have written and imported.

To declare it that way, you need to either use an @class declaration for your CurrencyConverter class or import the header file. Many developers consider the @class method more elegant. Figure 8.4 shows the interface file.

Image

FIGURE 8.4 Use static typing for your instance variable.

You do not need to make any changes to the implementation file. However, when you run the app and set a breakpoint before the allocation of the object, notice that although there still is no memory allocated for it, its type is CurrencyConverter, as shown in Figure 8.5.

Image

FIGURE 8.5 The variable is typed before it is allocated.

Because you have statically typed the variable, the compiler lets you know that you cannot send the display message to it, as shown in Figure 8.6. When you typed the variable as id, the compiler has no way of knowing if it can respond to a message. At runtime, you may replace the totally flexible id variable with an instance that might respond to the message. However, with a statically typed variable, the compiler knows that whatever you may replace it with must be an instance of CurrencyConverter or a subclass of it, and display is not a legal message.

Image

FIGURE 8.6 Use static typing for your instance variable.


Note

It is important to note that id is a typedef (specifically a typedef to a pointer to an instance of a class). When you statically type a variable, you provide a pointer to the class, so the declaration looks like

Click here to view code image

CurrencyConverter *currencyConverter;

Whether the space comes before or after the asterisk does not matter. It appears that placing the asterisk before the variable identifier rather than after the class name is more common.


Creating an Instance Variable for CurrencyConverter with a Superclass Name

There is a variation on the static typing example that you find frequently. In the previous example, the instance variable was statically typed with the class name—CurrencyConverter. You can statically type it as a class and then instantiate a subclass of that class. For example, you can declare the instance variable to be of class NSObject, as shown in Figure 8.7.

Image

FIGURE 8.7 Declare the instance variable as type NSObject.

This means that you still cannot send a display message to it because the compiler knows that display is not a method of NSObject, but you can send any of the NSObject messages.

You will come across issues like this, particularly in classes with a number of subclasses such as NSView and UIView. You frequently call methods of the superclass intending either to actually perform those methods or intending to perform code that overrides those methods in subclasses.

In effect, you can write for the class that statically types the instance variable, but at runtime, the class that is actually used may well be a subclass that you have created. The model of typing an instance variable with a superclass is very common.

If you are working with an instance that is declared as a superclass (UIView, for example) and you know that it is a particular subclass, you can cast it to the subclass. For example, if you have instance variables defined as

UIView *myView;
UITextView *myTextView;

you can cast and assign a myView object to myTextView, as follows, providing that the actual instance is, indeed, an UITextView:

myTextView = (UITextView*)myView;

Managing Instance Variable Visibility

As is the case in many object-oriented programming languages, you can restrict the visibility of a variable. Your three choices are the following:

Image Protected—The variable is visible to methods of the class in which it is declared as well as to methods in subclasses of that class. This is the default value.

Image Private—The variable is visible only to methods of the class in which it is declared.

Image Public—The variable is visible to all methods. This is very rarely used because it destroys the notion of encapsulation. Anyone could access this method.

Applying these visibility rules, you can have an interface such as the following:

@interface CurrencyConverter : NSObject {
  @public
  NSString  *dataSourceCopyrightString;

  @private
  NSDate    *lastDataSourceUpdate;

  @protected
  float     rate;
  float     amount;
  float     result;
}

Putting those rules together, you can see that the only variable that is always available is a copyright string. The underlying date of the source is restricted to members of the class. The floats that a client might be expected to access are protected. This is not an uncommon strategy.

Summary

In this hour, you have seen how to declare instance variables—variables that are present in each instance of a class. The basic syntax is the same as for any C variable declarations. However, you can use pointers to refer to instances of a class.

Q&A

Q. What is the difference between static typing and using id?

A. With id, variables are typed dynamically at runtime. The class of the instantiated object determines what methods are available. This slightly increases the overhead at runtime. Static typing removes that slight increase in overhead, and it allows error checking to be done by the compiler instead of by the runtime code.

Q. What is the purpose of casting an instance?

A. If you have an instance variable that is declared as a superclass of the object you are working with, you can cast the object down to the subclass.

Workshop

Quiz

1. If you change the superclass of a class, what precaution do you take in building the project?

2. When do you use the @public visibility rule?

3. How can you tell if an object has been allocated?

Quiz Answers

1. Clean the project.

2. This makes a variable accessible to anyone with access to the class. It is generally not used because it breaks the concept of encapsulation.

3. Using the debugger, set a breakpoint and check to see if memory has been allocated for the object.

Activities

Some of the sample code on developer.apple.com dates back a few years. Download some of the samples with modification dates from 2010 and earlier. Many of them use instance variables rather than properties. Make certain that you can understand their interfaces. Even if you switch over to properties for new projects, you will probably be modifying older code and need to understand the concepts presented in this hour.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.52.7