In this chapter you learn about some key concepts in object-oriented programming and start working with classes in Objective-C. You'll need to learn a little bit of terminology, and we'll keep it fairly informal. We'll also cover only some of the basic terms here because you can easily get overwhelmed. Refer to Appendix A, “Glossary,” at the end of this book for more precise definitions of these terms.
An object is a thing. Think about object-oriented programming as a thing and something you want to do to that thing. This is in contrast to a programming language such as C, known as a procedural programming language. In C, you typically think about what you want to do first and then you worry about the objects…almost the opposite from object-orientation.
Let's take an example from everyday life. Let's assume you own a car, which is obviously an object, and one that you own. You don't have just any car; you have a particular car that was manufactured in a factory, maybe in Detroit, maybe in Japan, or maybe someplace else. Your car has a vehicle identification number (VIN), which uniquely identifies that car.
In object-oriented parlance, your car is an instance of a car. And continuing with the terminology, car is the name of the class from which this instance was created. So, each time a new car is manufactured, a new instance from the class of cars is created, and each instance of the car is referred to as an object.
Your car might be silver, have a black interior, and be a convertible or hardtop, and so on. Additionally, you perform certain actions with your car. For example, you drive your car, fill it with gas, (hopefully) wash it, take it in for service, and so on. This is depicted in Table 3.1.
The actions listed in Table 3.1 can be done with you car, and they can also be done with other cars as well. For example, your sister drives her car, washes it, fills it with gas, and so on.
A unique occurrence of a class is an instance, and the actions that are performed on the instance are called methods. In some cases, a method can be applied to an instance of the class or to the class itself. For example, washing you car applies to an instance (in fact, all the methods listed in Table 3.1 would be considered instance methods). Finding out how many types of cars a manufacturer makes would apply to the class, so it would be a class method. Suppose you have two cars that came off the assembly line that are seemingly identical: They both have the same interior, same paint color, and so on. They might start out the same, but as each car is used by its respective owner, it acquires its own unique characteristics. For example, one car might end up with a scratch on it and the other might have more miles on it. Each instance or object contains not only information about its initial characteristics it acquired from the factory, but also its current characteristics. Those characteristics can change dynamically. As you drive your car, the gas tank becomes depleted, the car gets dirtier, and the tires get a little more worn.
Applying a method to an object can affect the state of that object. If your method is to “fill up my car with gas,” after that method is performed your car's gas tank will be full. The method will have affected the state of the car's gas tank.
The key concepts here are that objects are unique representations from a class, and each object contains some information (data) that is typically private to that object. The methods provide the means of accessing and changing that data.
The Objective-C programming language has the following particular syntax for applying methods to classes and instances:
[ ClassOrInstance method ];
In this syntax, a left bracket is followed by the name of a class or instance of that class, which is followed by one or more spaces, which is followed by the method you want to perform. Finally, it is closed off with a right bracket and a terminating semicolon. When you ask a class or an instance to perform some action, you say that you are sending it a message; the recipient of that message is called the receiver. So, another way to look at the general format described previously is as follows:
[ receiver message ] ;
Let's go back to the previous list and write everything in this new syntax. Before you do that, though, you need to get your new car. Go to the factory for that, like so:
yourCar = [Car new]; get a new car
You send a message to the Car
class (the receiver of the message) asking it to give you a new car. The resulting object (which represents your unique car), is then stored in the variable yourCar
. From now on, yourCar
can be used to refer to your instance of the car, which you got from the factory.
Because you went to the factory to get the car, the method new is called a factory or class method. The rest of the actions on your new car will be instance methods because they apply to your car. Here are some sample message expressions you might write for your car:
[yourCar prep]; get it ready for first-time use
[yourCar drive]; drive your car
[yourCar wash]; wash your car
[yourCar getGas]; put gas in your car if you need it
[yourCar service]; service your car
[yourCar topDown]; if it's a convertible
[yourCar topUp];
Your sister Sue can use the same methods for her own instance of a car:
[suesCar drive];
[suesCar wash];
[suesCar getGas];
This is one of the key concepts behind object-oriented programming (that is, applying the same methods to different objects), and you'll learn more about that later.
You probably won't need to work with cars in your programs. Your objects are likely to be computer-oriented things, such as windows, rectangles, pieces of text, or maybe even a calculator or a playlist of songs. And just like the methods used for your cars, your methods might look similar, as in the following:
Now it's time to define an actual class in Objective-C and learn how to work with instances of the class.
Once again, you'll learn procedure first. As a result, the actual program examples might not seem very practical. We'll get into more practical stuff later.
Suppose you needed to write a program to work with fractions. Maybe you needed to deal with adding, subtracting, multiplying them, and so on. If you didn't know about classes, you might start with a simple program that looked like this:
// Simple program to work with fractions
#import <stdio.h>
int main (int argc, char *argv[])
{
int numerator = 1;
int denominator = 3;
printf ("The fraction is %i/%i
", numerator, denominator);
return 0;
}
The fraction is 1/3
In Program 3.1 the fraction is represented in terms of its numerator and denominator. The first two lines in main
both declare the variables numerator
and denominator
as integers and assign them initial values of 1
and 3
, respectively. This is equivalent to the following lines:
int numerator, denominator;
numerator = 1;
denominator = 3;
We represented the fraction 1/3 by storing 1
into the variable numerator
and 3
into the variable denominator
. If you needed to store a lot of fractions in your program, this could be cumbersome. Each time you wanted to refer to the fraction, you'd have to refer to the corresponding numerator and denominator. And performing operations on these fractions would be just as awkward.
It would be better if you could define a fraction as a single entity and collectively refer to its numerator and denominator with a single name, such as myFraction
. You can do that in Objective-C, and it starts by defining a new class.
Program 3.2 duplicates the functionality of Program 3.1 using a new class called Fraction
. Here, then, is the program followed by a detailed explanation of how it works.
// Program to work with fractions – class version
#import <stdio.h>
#import <objc/Object.h>
//------- @interface section -------
@interface Fraction: Object
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
@end
//------- @implementation section -------
@implementation Fraction;
-(void) print
{
printf (" %i/%i ", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
@end
//------- program section -------
int main (int argc, char *argv[])
{
Fraction *myFraction;
// Create an instance of a Fraction
myFraction = [Fraction alloc];
myFraction = [myFraction init];
// Set fraction to 1/3
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
// Display the fraction using the print method
printf ("The value of myFraction is:");
[myFraction print];
printf ("
");
[myFraction free];
return 0;
}
The value of myFraction is: 1/3
As you can see from the comments in Program 3.2, the program is logically divided into three sections, as depicted in Figure 3.1:
• @interface
section
• @implementation
section
• program
section
Figure 3.1. Program structure.
The @interface
section describes the class, its data components, and its methods, whereas the @implementation
section contains the actual code that implements these methods. Finally, the program
section contains the program code to carry out the intended purpose of the program.
Each of these sections is a part of every Objective-C program, even though you might not need to write each section yourself. As you'll see, each section is typically put in its own file. For now, however, we'll keep it all together in a single file.
@interface
SectionWhen you define a new class, you have to do a few things. First, you have to tell the Objective-C compiler where the class came from. That is, you have to name its parent class. Second, you have to specify what type of data is to be stored in the objects of this class. That is, you have to describe the data that members of the class will contain. These members are called the instance variables. Finally, you need to define the type of operations, or methods, that can be used when working with objects from this class. This is all done in a special section of the program called the @interface
section. The general format of this section looks like this:
@interface NewClassName: ParentClassName
{
memberDeclarations;
}
methodDeclarations;
@end
By convention, class names begin with an uppercase letter, even though it's not required. This enables someone reading your program to distinguish class names from other types of variables by simply looking at the first character of the name. Let's take a short diversion to talk a little bit about forming names in Objective-C.
In Chapter 2, “Programming in Objective-C,” you used several variables to store integer values. For example, you used the variable sum
in Program 2.4 to store the result of the addition of the two integers, 50 and 25.
The Objective-C language allows data types other than just integers to be stored in variables as well, provided the proper declaration for the variable is made before it is used in the program. Variables can be used to store floating-point numbers, characters, and even objects (or more precisely, references to objects).
The rules for forming names are quite simple: They must begin with a letter or underscore (_
), and they can be followed by any combination of letters (upper- or lowercase), underscores, or the digits 0–9. The following is a list of valid names:
• sum
• pieceFlag
• i
• myRectangle
• numberOfMoves
• _sysFlag
• ChessPiece
On the other hand, the following names are not valid for the stated reasons:
• sum$value
—$
is not a valid character.
• piece flag
—Embedded spaces are not permitted.
• 3Spencer
—Names can't start with a number.
• int
—This is a reserved word.
int
cannot be used as a variable name because its use has a special meaning to the Objective-C compiler. This use is known as a reserved name or reserved word. In general, any name that has special significance to the Objective-C compiler cannot be used as a variable name. Appendix B, “Objective-C Language Summary,” provides a complete list of such reserved names.
You should always remember that upper- and lowercase letters are distinct in Objective-C. Therefore, the variable names sum
, Sum
, and SUM
each refer to a different variable. As noted, when naming a class, start it with a capital letter. Instance variables, objects, and method names, on the other hand, typically begin with lowercase letters. To aid readability, capital letters are used inside names to indicate the start of a new word, as in the following examples:
When deciding on a name, keep one recommendation in mind: Don't be lazy. Pick names that reflect the intended use of the variable or object. The reasons are obvious. Just as with the comment statement, meaningful names can dramatically increase the readability of a program and will pay off in the debug and documentation phases. In fact, the documentation task will probably be greatly reduced because the program will be more self-explanatory.
Here again is the @interface
section from Program 3.2:
//------- @interface section -------
@interface Fraction: Object
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
@end
The name of the new class (NewClassName
) is Fraction
, and its parent class is Object
. (We'll talk in greater detail about parent classes in Chapter 8, “Inheritance.”) The Object
class is defined in the file objc/Object.h
, which is why that file (along with stdio.h)
is imported at the beginning of Program 3.2:
#import <objc/Object.h>
The memberDeclarations
section specifies what types of data are stored in a Fraction
, as well as the names of those data types. As you can see, this section is enclosed inside its own set of curly braces. For your Fraction
class, the declarations
int numerator;
int denominator;
say that a Fraction
object has two integer members called numerator
and denominator
.
The members declared in this section are known as the instance variables. As you'll see, each time you create a new object, a new and unique set of instance variables also is created. Therefore, if you have two Fractions
, one called fracA
and another called fracB
, each will have its own set of instance variables. That is, fracA
and fracB
each will have its own separate numerator
and denominator
. The Objective-C system automatically keeps track of this for you, which is one of the nicer things about working with objects.
You have to define methods to work with your Fraction
s. You'll need to be able to set the value of a fraction to a particular value. Because you won't have direct access to the internal representation of a fraction (in other words, direct access to its instance variables), you must write methods to set the numerator and denominator. You'll also write a method called print
that will display the value of a fraction. Here's what the declaration for the print
method looks like in the interface file:
-(void) print;
The leading minus sign (-
) tells the Objective-C compiler that the method is an instance method. The only other option is a plus sign (+
), which indicates a class method. A class method is one that performs some operation on the class itself, such as creating a new instance of the class. This is similar to manufacturing a new car, in that the car is the class and you want to create a new one—which would be a class method.
An instance method performs some operation on a particular instance of a class, such as setting its value, retrieving its value, displaying its value, and so on. Referring to the car example, after you have manufactured the car, you might need to fill it with gas. The operation of filling it with gas is performed on a particular car, so it would be analogous to an instance method.
When you declare a new method, you have to tell the Objective-C compiler whether the method returns a value, and if it does, what type of value it returns. This is done by enclosing the return type in parentheses after the leading minus or plus sign. So, the declaration
-(int) getNumerator;
specifies that the instance method called getNumerator
returns an integer value. Similarly, the line
-(double) getDoubleValue;
declares a method that returns a double precision value. (You'll learn more about this data type in Chapter 4, “Data Types and Expressions.”)
A value is returned from a method using the Objective-C return
statement, similar to the way in which we returned a value from main
in previous program examples.
If the method returns no value, you indicate that using the type void
, as in the following:
-(void) print;
This declares an instance method called print
that returns no value. In such a case, you do not need to execute a return
statement at the end of your method. Alternatively, you can execute a return
without any specified value, as in the following:
return;
You don't need to specify a return type for your methods, although it's better programming practice if you do. If none is specified, id
is the default. You'll learn more about the id
data type in a later chapter. Basically, the id
type can be used to refer to any type of object.
Two other methods are declared in the @interface
section from Program 3.2:
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
These are both instance methods that return no value. Each method takes an integer argument, which is indicated by the (int)
in front of the argument name. In the case of setNumerator
, the name of the argument is n
. This name is arbitrary, and it is the name the method uses to refer to the argument. Therefore, the declaration of setNumerator
specifies that one integer argument called n
will be passed to the method and that no value is returned. This is similar for setDenominator
, except the name of its argument is d
.
Notice the syntax of the declaration for these methods. Each method name ends with a colon, which tells the Objective-C compiler that the method expects to see an argument. Next, the type of the argument is specified, enclosed in a set of parentheses, in much the same way the return type is specified for the method itself. Finally, the symbolic name to be used to identify that argument in the method is specified. The entire declaration is terminated with a semicolon. This syntax is depicted in Figure 3.2.
Figure 3.2. Declaring a method.
When a method takes an argument, you also append a colon to the method name when referring to the method. setNumerator:
and setDenominator:
would therefore be the correct way to identify these two methods—each of which takes a single argument. Also, the identification of the print
method without a trailing colon indicates that this method does not take any arguments. In Chapter 7, “More on Classes,” you'll see how methods that take more than one argument are identified.
@implementation
SectionAs noted, the @implementation
section contains the actual code for the methods you declared in the @interface
section. Just as a point of terminology, you say that you declare the methods in the @interface
section and that you define them (that is, give the actual code) in the @implementation
section.
The general format for the @implementation
section is as follows:
@implementation NewClassName;
methodDefinitions;
@end
NewClassName
is the same name that was used for the class in the @interface
section. You can use the trailing colon followed by the parent class name, as we did in the @interface
section:
@implementation Fraction: Object;
However, it's optional and typically not done.
The methodDefinitions
part of the @implementation
section contains the code for each method specified in the @interface
section. Similar to the @interface
section, each method's definition starts by identifying the type of method (class or instance), its return type, and its arguments and their types. However, instead of the line ending with a semicolon, the code for the method follows, enclosed inside a set of curly braces.
Here's the @implementation
section from Program 3.2:
//------- @implementation section -------
@implementation Fraction;
-(void) print
{
printf (" %i/%i ", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
@end
The print
method uses printf
to display the values of the instance variables numerator
and denominator
. But to which numerator and denominator does this method refer? It refers to the instance variables contained in the object that is the receiver of the message. That's an important concept, and we'll return to it shortly.
The setNumerator:
method takes the integer argument you called n
and simply stores it in the instance variable numerator
. Similarly, setDenominator:
stores the value of its argument d
in the instance variable denominator
.
The program
section contains the code to solve your particular problem, which can be spread out across many files, if necessary. Somewhere you must have a routine called main
, as we've previously noted. That's where your program always begins execution. Here is the program section from Program 3.2:
//------- program section -------
int main (int argc, char *argv[])
{
Fraction *myFraction;
// Create an instance of a Fraction
myFraction = [Fraction alloc];
myFraction = [myFraction init];
// Set fraction to 1/3
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
// Display the fraction using the print method
printf ("The value of myFraction is:");
[myFraction print];
printf ("
");
[myFraction free];
return 0;
}
Inside main
you define a variable called myFraction
with the following line:
Fraction *myFraction;
This line says that myFraction
is an object of type Fraction
; that is, myFraction
is used to store values from your new Fraction
class. The asterisk (*
) in front of myFraction
is required, but don't worry about its purpose now. Technically, it says that myFraction
is actually a reference (or pointer) to a Fraction
.
Now that you have an object to store a Fraction
, you need to create one, just like you ask the factory to build you a new car. This is done with the following line:
myFraction = [Fraction alloc];
alloc
is short for allocate. You want to allocate memory storage space for a new fraction. The expression
[Fraction alloc]
sends a message to your newly created Fraction
class. You are asking the Fraction
class to apply the alloc
method, but you never defined an alloc
method, so where did it come from? The method was inherited from a parent class. Chapter 8 deals with this topic in detail.
When you send the alloc
message to a class, you get back a new instance of that class. In Program 3.2, the returned value is stored inside your variable myFraction
. The alloc
method is guaranteed to zero out all of an object's instance variables. However, that does not mean that the object has been properly initialized for use. You need to initialize an object after you allocate it.
This is done with the next statement in Program 3.2, that reads as follows:
myFraction = [myFraction init];
Again, you are using a method here that you didn't write yourself. The init
method initializes the instance of a class. Note that you are sending the init
message to myFraction
. That is, you want to initialize a specific Fraction
object here, so you don't send it to the class—you send it to an instance of the class. Make sure you understand this point before continuing.
The init
method also returns a value, namely the initialized object. You take the return value and store it in your Fraction
variable myFraction
.
The two-line sequence of allocating a new instance of class and then initializing it is done so often in Objective-C that the two messages are typically combined, as follows:
myFraction = [[Fraction alloc] init];
The inner message expression
[Fraction alloc]
is evaluated first. As you know, the result of this message expression is the actual Fraction
that is allocated. Instead of storing the result of the allocation in a variable as you did before, you directly apply the init
method to it. So, again, first you allocate a new Fraction
and then you initialize it. The result of the initialization is then assigned to the myFraction
variable.
As a final shorthand technique, the allocation and initialization is often incorporated directly into the declaration line, as in the following:
Fraction *myFraction = [[Fraction alloc] init];
This coding style is used often throughout the remainder of this book, so it's important that you understand it.
Returning to Program 3.2, you are now ready to set the value of your fraction. The program lines
// Set fraction to 1/3
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
do just that. The first message statement sends the setNumerator:
message to myFraction
. The argument that is supplied is the value 1
. Control is then sent to the setNumerator:
method you defined for your Fraction
class. The Objective-C system knows that it is the method from this class to use because it knows that myFraction
is an object from the Fraction
class.
Inside the setNumerator:
method, the passed value of 1
is stored inside the variable n
. The single program line in that method takes that value and stores it in the instance variable numerator
. So, you have effectively set the numerator of myFraction
to 1
.
The message that invokes the setDenominator:
method on myFraction
follows next. The argument of 3
is assigned to the variable d
inside the setDenominator:
method. This value is then stored inside the denominator
instance variable, thus completing the assignment of the value 1/3
to myFraction
. Now you're ready to display the value of your fraction, which is done with the following lines of code from Program 3.2:
// display the fraction using the print method
printf ("The value of myFraction is:");
[myFraction print];
printf ("
");
The first printf
call simply displays the following text:
The value of myFraction is:
A newline is not appended to the end of the line so that the value of the fraction as displayed by the print
method appears on the same line. The print
method is invoked with the following message expression:
[myFraction print];
Inside the print
method, the values of the instance variables numerator
and denominator
are displayed, separated by a slash character. The final printf
call back in main
simply displays a newline character.
The last message in the program,
[myFraction free];
frees the memory that was used for the Fraction
object. This is a critical part of good programming style. Whenever you create a new object, you are asking for memory to be allocated for that object. Also, when you're done with the object, you are responsible for releasing the memory it uses. Although it's true that the memory will be released when your program terminates anyway, after you start developing more sophisticated applications, you can end up working with hundreds (or thousands) of objects that consume a lot of memory. Waiting for the program to terminate for the memory to be released is wasteful of memory, can slow your program's execution, and is not good programming style. So, get into the habit right now!
It seems as if you had to write a lot more code to duplicate in Program 3.2 what you did in Program 3.1. That's true for this simple example here; however, the ultimate goal in working with objects is to make your programs easier to write, maintain, and extend. You'll realize that later.
The last example in this chapter shows how you can work with more than one fraction in your program. In Program 3.3, you set one fraction to 2/3, set another to 3/7, and display them both.
// Program to work with fractions – cont'd
#import <stdio.h>
#import <objc/Object.h>
//------- @interface section -------
@interface Fraction: Object
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
@end
//------- @implementation section -------
@implementation Fraction;
-(void) print
{
printf (" %i/%i ", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
@end
//------- program section -------
int main (int argc, char *argv[])
{
Fraction *frac1 = [[Fraction alloc] init];
Fraction *frac2 = [[Fraction alloc] init];
// Set 1st fraction to 2/3
[frac1 setNumerator: 2];
[frac1 setDenominator: 3];
// Set 2nd fraction to 3/7
[frac2 setNumerator: 3];
[frac2 setDenominator: 7];
// Display the fractions
printf ("First fraction is:");
[frac1 print];
printf ("
");
printf ("Second fraction is:");
[frac2 print];
printf ("
");
[frac1 free];
[frac2 free];
return 0;
}
First fraction is: 2/3
Second fraction is: 3/7
The @interface
and @implementation
sections remain unchanged from Program 3.2. The program creates two Fraction
objects called frac1
and frac2
and then assigns the value 2/3
to the first fraction and 3/7
to the second. Realize that, when the setNumerator:
method is applied to frac1
to set its numerator to 2
, the instance variable frac1
gets its instance variable numerator
set to 2
. Also, when frac2
uses the same method to set its numerator to 3
, its distinct instance variable numerator
is set to the value 3
. Each time you create a new object, it gets its own distinct set of instance variables. This is depicted in Figure 3.3.
Figure 3.3. Unique instance variables.
Based on which object is getting sent the message, the correct instance variables are referenced. Therefore, in
[frac1 setNumerator: 2];
it is frac1
's numerator
that is referenced whenever setNumerator:
uses the name numerator
inside the method. That's because frac1
is the receiver of the message.
You've seen how the methods that deal with fractions can access the two instance variables numerator
and denominator
directly by name. In fact, an instance method can always directly access its instance variables. A class method can't, however, because it's dealing only with the class itself and not with any instances of the class (think about that for a second). But what if you wanted to access your instance variables from someplace else—for example, from inside your main
routine? You can't do that directly because they are hidden. The fact that they are hidden from you is a key concept called data encapsulation. It enables someone writing class definitions to extend and modify his class definitions without worrying about whether programmers (that is, users of the class) are tinkering with the internal details of the class. Data encapsulation provides a nice layer of insulation between the programmer and the class developer.
You can access your instance variables in a clean way by writing special methods to retrieve their values. For example, you'll create two new methods called, appropriately enough, numerator
and denominator
to access the corresponding instance variables of the Fraction
that is the receiver of the message. The result will be the corresponding integer value, which you will return. Here are the declarations for your two new methods:
-(int) numerator;
-(int) denominator;
And here are the definitions:
-(int) numerator
{
return numerator;
}
-(int) denominator
{
return denominator;
}
Note that the names of the methods and the instance variables they access are the same. There's no problem doing this; in fact, it is common practice. Program 3.4 tests your two new methods.
// Program to access instance variables – cont'd
#import <stdio.h>
#import <objc/Object.h>
//------- @interface section -------
@interface Fraction: Object
{
int numerator;
int denominator;
}
-(void) print;
-(void) setNumerator: (int) n;
-(void) setDenominator: (int) d;
-(int) numerator;
-(int) denominator;
@end
//------- @implementation section -------
@implementation Fraction;
-(void) print
{
printf (" %i/%i ", numerator, denominator);
}
-(void) setNumerator: (int) n
{
numerator = n;
}
-(void) setDenominator: (int) d
{
denominator = d;
}
-(int) numerator
{
return numerator;
}
-(int) denominator
{
return denominator;
}
@end
//------- program section -------
int main (int argc, char *argv[])
{
Fraction *myFraction = [[Fraction alloc] init];
// Set fraction to 1/3
[myFraction setNumerator: 1];
[myFraction setDenominator: 3];
// Display the fraction using our two new methods
printf ("The value of myFraction is: %i/%i
",
[myFraction numerator], [myFraction denominator]);
[myFraction free];
return 0;
}
The value of myFraction is 1/3
The printf
statement
printf ("The value of myFraction is: %i/%i
",
[myFraction numerator], [myFraction denominator]);
displays the results of sending two messages to myFraction
: the first to retrieve the value of its numerator
, and the second the value of its denominator
.
Incidentally, methods that set the values of instance variables are often collectively referred to as setters, and methods used to retrieve the values of instance variables are called getters. For the Fraction
class, setNumerator:
and setDenominator:
are the setters and numerator
and denominator
are the getters.
It should also be pointed out here that there is also a method called new
that combines the actions of an alloc
and init
. So, the line
Fraction *myFraction = [Fraction new];
could be used to allocate and initialize a new Fraction
. It's generally better to use the two-step allocation and initialization approach so you conceptually understand that two distinct events are occurring: You're first creating a new object and then you're initializing it.
Now you know how to define your own class, create objects or instances of that class, and send messages to those objects. We'll return to the Fraction
class in later chapters. You'll learn how to pass multiple arguments to your methods, how to divide your class definitions into separate files, and also about key concepts such as inheritance and dynamic binding. However, now it's time to learn more about data types and writing expressions in Objective-C. First, try the exercises that follow to test your understanding of the important points covered in this chapter.
Int playNextSong 6_05
_calloc Xx alphaBetaRoutine
clearScreen _1312 z
ReInitialize _ A$
[instance method];
to rewrite your list in this format.
Vehicle
and an object called myVehicle
that could be either a Car
, a Motorcycle
, or a Boat
. What if you wrote the following:
[myVehicle prep];
[myVehicle getGas];
[myVehicle service];
Do you see any advantages of being able to apply an action to an object that could be from one of several classes?
Point
that will hold a Cartesian coordinate (x, y), where x and y are integers. Define methods to individually set the x and y coordinates of a point and retrieve their values. Write an Objective-C program to implement your new class and test it.18.217.147.193