Programming in Objective-C 2.0 edition phần 4 pot

59 366 0
Programming in Objective-C 2.0 edition phần 4 pot

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

168 Chapter 8 Inheritance Using the @class directive is more efficient because the compiler doesn’t need to process the entire XYPoint.h file (even though it is quite small); it just needs to know that XYPoint is the name of a class. If you need to reference one of the XYPoint classes meth- ods, the @class directive does not suffice because the compiler would need more infor- mation; it would need to know how many arguments the method takes, what their types are, and what the method’s return type is. Let’s fill in the blanks for your new XYPoint class and Rectangle methods so you can test everything in a program. First, Program 8.4 shows the implementation file for your XYPoint class. First, Program 8.4 shows the new methods for the Rectangle class. Program 8.4 Rectangle.m Added Methods #import “XYPoint.h” -(void) setOrigin: (XYPoint *) pt { origin = pt; } -(XYPoint *) origin { return origin; } @end Following are the complete XYPoint and Rectangle class definitions, followed by a test program to try them out. Program 8.4 XPoint.h Interface File #import <Foundation/Foundation.h> @interface XYPoint: NSObject { int x; int y; } @property int x, y; -(void) setX: (int) xVal andY: (int) yVal; @end Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 169 Extension Through Inheritance: Adding New Methods Program 8.4 XYPoint.m Implementation File #import “XYPoint.h” @implementation XYPoint @synthesize x, y; -(void) setX: (int) xVal andY: (int) yVal { x = xVal; y = yVal; } @end Program 8.4 Rectangle.h Interface File #import <Foundation/Foundation.h> @class XYPoint; @interface Rectangle: NSObject { int width; int height; XYPoint *origin; } @property int width, height; -(XYPoint *) origin; -(void) setOrigin: (XYPoint *) pt; -(void) setWidth: (int) w andHeight: (int) h; -(int) area; -(int) perimeter; @end Program 8.4 Rectangle.m Implementation File #import “Rectangle.h” @implementation Rectangle @synthesize width, height; -(void) setWidth: (int) w andHeight: (int) h Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 170 Chapter 8 Inheritance { width = w; height = h; } –(void) setOrigin: (Point *) pt { origin = pt; } –(int) area { return width * height; } –(int) perimeter { return (width + height) * 2; } –(Point *) origin { return origin; } @end Program 8.4 Test Program #import “Rectangle.h” #import “XYPoint.h” int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Rectangle *myRect = [[Rectangle alloc] init]; XYPoint *myPoint = [[XYPoint alloc] init]; [myPoint setX: 100 andY: 200]; [myRect setWidth: 5 andHeight: 8]; myRect.origin = myPoint; NSLog (@”Rectangle w = %i, h = %i”, myRect.width, myRect.height); Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 171 Extension Through Inheritance: Adding New Methods NSLog (@”Origin at (%i, %i)”, myRect.origin.x, myRect.origin.y); NSLog (@”Area = %i, Perimeter = %i”, [myRect area], [myRect perimeter]); [myRect release]; [myPoint release]; [pool drain]; return 0; } Program 8.4 Output Rectangle w = 5, h = 8 Origin at (100, 200) Area = 40, Perimeter = 26 Inside the main routine, you allocated and initialized a rectangle identified as myRect and a point called myPoint. Using the setX:andY: method, you set myPoint to (100, 200) .After setting the width and the height of the rectangle to 5 and 8, respectively, you invoked the setOrigin method to set the rectangle’s origin to the point indicated by myPoint.The three printf calls then retrieve and print the values.The expression myRect.origin.x takes the XYPoint object returned by the accessor method origin method and applies the dot operator to get the x-coordinate of the rectangle’s origin. In a similar manner, the fol- lowing expression retrieves the y-coordinate of the rectangle’s origin: myRect.origin.y Classes Owning Their Objects Can you explain the output from Program 8.5? Program 8.5 #import “Rectangle.h” #import “XYPoint.h” int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Rectangle *myRect = [[Rectangle alloc] init]; XYPoint *myPoint = [[XYPoint alloc] init]; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 172 Chapter 8 Inheritance 100 200 x y myPoint Figure 8.5 The XYPoint myPoint in memory [myPoint setX: 100 andY: 200]; [myRect setWidth: 5 andHeight: 8]; myRect.origin = myPoint; NSLog (@”Origin at (%i, %i)”, myRect.origin.x, myRect.origin.y); [myPoint setX: 50 andY: 50]; NSLog (@”Origin at (%i, %i)”, myRect.origin.x, myRect.origin.y); [myRect release]; [myPoint release]; [pool drain]; return 0; } Program 8.5 Output Origin at (100, 200) Origin at (50, 50) You changed the XYPoint myPoint from (100, 200) in the program to (50, 50), and apparently it also changed the rectangle’s origin! But why did that happen? You didn’t explicitly reset the rectangle’s origin, so why did the rectangle’s origin change? If you go back to the definition of your setOrigin: method, perhaps you’ll see why: -(void) setOrigin: (XYPoint *) pt { origin = pt; } When the setOrigin: method is invoked with the expression myRect.origin = myPoint; the value of myPoint is passed as the argument to the method.This value points to where this XYPoint object is stored in memory, as depicted in Figure 8.5. Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 173 Extension Through Inheritance: Adding New Methods 100 200 x y myPoint pt Figure 8.6 Passing the rectangle’s origin to the method That value stored inside myPoint, which is a pointer into memory, is copied into the local variable pt as defined inside the method. Now both pt and myPoint reference the same data stored in memory. Figure 8.6 illustrates this. When the origin variable is set to pt inside the method, the pointer stored inside pt is copied into the instance variable origin, as depic ed in Figure 8.7. Because myPoint and the origin variable stored in myRect reference the same area in memory (as does the local variable pt), when you subsequently change the value of myPoint to (50, 50), the rectangle’s origin is changed as well. You can avoid this problem by modifying the setOrigin: method so that it allocates its own point and sets the origin to that point.This is shown here: -(void) setOrigin: (XYPoint *) pt { origin = [[XYPoint alloc] init]; [origin setX: pt.x andY: pt.y]; } The method first allocates and initializes a new XYPoint.The message expression 100 200 x y 5 8 w h origin myPoint pt myRect Figure 8.7 Setting the rectangle’s origin Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 174 Chapter 8 Inheritance Figure 8.8 Compiler error messages [origin setX: pt.x andY: pt.y]; sets the newly allocated XYPoint to the x, y coordinate of the argument to the method. Study this message expression until you fully understand how it works. The change to the setOrigin: method means that each Rectangle instance now owns its origin XYPoint instance. Even though it is now responsible for allocating the memory for that XYPoint, it should also now become responsible for releasing that mem- ory. In general, when a class contains other objects, at times you will want to have it own some or all of those objects. In the case of a rectangle, it makes sense for the Rectangle class to own its origin because that is a basic attribute of a rectangle. But how do you release the memory used by your origin? Releasing the rectangle’s memory does not also release the memory you allocated for the origin. One way to re- lease the memory is to insert a line such as the following into main: [[myRect origin] release]; This releases the XYPoint object that the origin method returns.You must do this be- fore you release the memory for the Rectangle object itself because none of the variables contained in an object is valid after an object’s memory is released. So the correct code se- quence would be as follows: [[myRect origin] release]; // Release the origin’s memory [myRect release]; // Release the rectangle’s memory It’s a bit of a burden to have to remember to release the origin’s memory yourself.After all, you weren’t the one who allocated it; the Rectangle class did. In the next section, “Overriding Methods,” you learn how to have the Rectangle release the memory. With your modified method, recompiling and rerunning Program 8.5 produces the er- ror messages shown as Figure 8.8. Oops! The problem here is that you’ve used some methods from the XYPoint class in your modified method, so now the compiler needs more information about it than the @class directive provides. In this case, you must go back and replace that directive with an import instead, like so: #import “XYPoint.h” Program 8.5B Output Origin at (100, 200) Origin at (100, 200) Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 175 Overriding Methods That’s better.This time, changing the value of myPoint to (50, 50) inside main had no effect on the rectangle’s origin because a copy of the point was created inside the Rectangle’s setOrigin: method. Incidentally, we didn’t synthesize the origin methods here because the synthesized set- ter setOrigin: method would have behaved just like the one you originally wrote.That is, by default, the action of a synthesized setter is to simply copy the object pointer, not the object itself. You can synthesize a different type of setter method that instead does make a copy of the object. However, to do that, you need to learn how to write a special copying method.We revisit this topic in Chapter 17,“Memory Management.” Overriding Methods We noted earlier in this chapter that you can’t remove or subtract methods through inher- itance. However, you can change the definition of an inherited method by overriding it. Returning to your two classes, ClassA and ClassB, assume that you want to write your own initVar method for ClassB.You already know that ClassB will inherit the initVar method defined in ClassA, but can you make a new method with the same name to replace the inherited method? The answer is yes, and you do so simply by defin- ing a new method with the same name.A method defined with the same name as that of a parent class replaces, or overrides, the inherited definition.Your new method must have the same return type and take the same number and type of arguments as the method you are overriding. Program 8.6 shows a simple example to illustrate this concept. Program 8.6 // Overriding Methods #import <Foundation/Foundation.h> // ClassA declaration and definition @interface ClassA: NSObject { int x; } -(void) initVar; @end @implementation ClassA -(void) initVar { x = 100; } Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 176 Chapter 8 Inheritance @end // ClassB declaration and definition @interface ClassB: ClassA -(void) initVar; -(void) printVar; @end @implementation ClassB -(void) initVar // added method { x = 200; } -(void) printVar { NSLog (@”x = %i”, x); } @end int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ClassB *b = [[ClassB alloc] init]; [b initVar]; // uses overriding method in B [b printVar]; // reveal value of x; [b release]; [pool drain]; return 0; } Program 8.6 Output x = 200 Clearly, the message [b initVar]; causes the initVar method defined in ClassB to be used, and not the one defined in ClassA, as was the case with the previous example. Figure 8.9 illustrates this. Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com 177 Overriding Methods Class Instance Variables Methods Object ClassA ClassB x x initVar initVar printVar Figure 8.9 Overriding the initVar method Which Method Is Selected? We covered how the system searches up the hierarchy for a method to apply to an object. If you have methods in different classes with the same name, the correct method is chosen based on the class of the receiver of the message. Program 8.7 uses the same class defini- tion for ClassA and ClassB as before. Program 8.7 #import <Foundation/Foundation.h> // insert definitions for ClassA and ClassB here int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ClassA *a = [[ClassA alloc] init]; ClassB *b = [[ClassB alloc] init]; [a initVar]; // uses ClassA method [a printVar]; // reveal value of x; [b initVar]; // use overriding ClassB method [b printVar]; // reveal value of x; [a release]; [b release]; [pool drain]; return 0; } You’ll get this warning message when you build this program: warning: ‘ClassA’ may not respond to ‘-printVar’ What happened here? We talked about this in an earlier section.Take a look at the dec- laration for ClassA: // ClassA declaration and definition Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com [...]... %i”, y); } @end int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ClassB *b = [[ClassB alloc] init]; [b initVar ; // uses overriding method in ClassB [b printVar]; // reveal values of x and y; [b release]; [pool drain]; return 0; } Program 8.8 Output x = 200 y = 300 The ClassB object b is initialized by invoking the initVar method defined within ClassB Recall... origin before you allocate and assign the new one For example, consider the following code sequence: myRect.origin = startPoint; Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com Extension Through Inheritance: Adding New Instance Variables myRect.origin = endPoint; [startPoint release]; [endPoint release]; [myRect release]; The copy of the XYPoint startPoint stored in the origin... origin (endPoint) that is stored there.That origin is released properly when the rectangle itself is released, based on your new release method You would have to ensure that, before you set a new origin in your rectangle, the old one was released.You could handle this in the setOrigin: method, as follows: -(void) setOrigin: (XYPoint *) pt { if (origin) [origin release]; origin = [[XYPoint alloc] init];... you allocate, without having to worry about the XYPoint objects they contain.The two release messages shown in Program 8.5 will now suffice to release all the objects you allocated in the program, including the XYPoint object that setOrigin: creates: [myRect release]; [myPoint release]; One issue remains: If you set the origin of a single Rectangle object to different values during the execution of your... 300.Write a test routine that declares ClassA, ClassB, and ClassC objects and invokes their corresponding initVar methods 2 When dealing with higher-resolution devices, you might need to use a coordinate system that enables you to specify points as floating-point values instead of as simple integers Modify the XYPoint and Rectangle classes from this chapter to deal with floating-point numbers.The rectangle’s... ways, but we don’t go into that here—besides, it’s not good programming practice Returning to our example, let’s add a printVar method to ClassA so you can display the value of its instance variables: // ClassA declaration and definition @interface ClassA: NSObject { int x; } -(void) initVar; -(void) printVar; @end @implementation ClassA -(void) initVar { x = 100; } -(void) printVar { NSLog (@”x =... of instance variables #import // Class A declaration and definition @interface ClassA: NSObject { int x; } -(void) initVar; @end @implementation ClassA -(void) initVar { x = 100; } @end // ClassB declaration and definition @interface ClassB: ClassA { int y; } -(void) initVar; -(void) printVar; @end @implementation ClassB -(void) initVar { x = 200; y = 300; } -(void) printVar... http://www.simpopdf.com 9 Polymorphism, Dynamic Typing, and Dynamic Binding In thispowerful you’ll learn about the features of distinguish it fromlanguage thatobject-it chapter, the Objective-C make such a programming language and that some other oriented programming languages such as C++.This chapter describes three key concepts: polymorphism, dynamic typing, and dynamic binding Polymorphism enables programs to... object-oriented programming in Objective-C As an exercise, consider removing the printVar method from ClassB.Would this work? Why or why not? ClassB Overriding the dealloc Method and the Keyword super Now that you know how to override methods, let’s return to Program 8.5B to learn a better approach to releasing the memory occupied by the origin.The setOrigin: method now allocates its own XYPoint origin object,... to invoke? That is, when it encounters the message expression [dataValue print]; how does it know which print method to invoke? You have print methods defined for both the Fraction and Complex classes As noted previously, the answer lies in the fact that the Objective-C system always keeps track of the class to which an object belongs It also lies in the concepts of dynamic typing and dynamic binding—that . alloc] init]; [myRect setWidth: 10 andHeight: 3]; [myRect draw]; [myRect release]; would produce the following output: ————— || || || ————— w = 25 0 w = 100 h = 75 h = 1 80 ( 20 0, 4 20 ) ( 40 0 , 300 ) Figure. release]; [myPoint release]; [pool drain]; return 0; } Program 8 .4 Output Rectangle w = 5, h = 8 Origin at ( 100 , 20 0) Area = 40 , Perimeter = 26 Inside the main routine, you allocated and initialized. setX: 50 andY: 50] ; NSLog (@”Origin at (%i, %i)”, myRect.origin.x, myRect.origin.y); [myRect release]; [myPoint release]; [pool drain]; return 0; } Program 8.5 Output Origin at ( 100 , 20 0) Origin

Ngày đăng: 12/08/2014, 23:22

Từ khóa liên quan

Mục lục

  • About the Author

  • About the Technical Reviewers

  • We Want to Hear from You!

  • Reader Services

  • Introduction

    • What You Will Learn from This Book

    • How This Book Is Organized

    • Acknowledgments

    • The Objective-C 2.0 Language

      • Programming in Objective-C

        • Compiling and Running Programs

        • Explanation of Your First Program

        • Displaying the Values of Variables

        • Summary

        • Exercises

        • Classes, Objects, and Methods

          • What Is an Object, Anyway?

          • Instances and Methods

          • An Objective-C Class for Working with Fractions

          • The @interface Section

          • The @implementation Section

          • The program Section

          • Accessing Instance Variables and Data Encapsulation

          • Summary

Tài liệu cùng người dùng

Tài liệu liên quan