Objective C - Week 5 - Thursday

  • Uploaded by: William Smith
  • 0
  • 0
  • June 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Objective C - Week 5 - Thursday as PDF for free.

More details

  • Words: 3,371
  • Pages: 63
September 17, 2009

Inheritance & Polymorphism

September 17, 2009

September 17, 2009

September 17, 2009

@interface ClassA: NSObject {    int  x; } -(void) initVar; @end @implementation ClassA -(void) initVar {   x = 100; } @end

September 17, 2009

@interface ClassB : ClassA -(void) printVar; @end @implementation ClassB -(void) printVar {   NSLog (@"x = %i", x); } @end

September 17, 2009

int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; ClassB

*b = [[ClassB alloc] init];

[b initVar]; [b printVar]; [b release]; [pool drain]; return 0; }

// will use inherited method // reveal value of x;

September 17, 2009

Output? X=100

September 17, 2009

Finding the Right Method When you send a message to an object, you might wonder how the correct method is chosen to apply to that object. The rules are actually quite simple. First, the class to which the object belongs is checked to see whether a method is explicitly defined in that class with the specific name. If it is, that's the method that is used. If it's not defined there, the parent class is checked. If the method is defined there, that's what is used. If not, the search continues. Parent classes are checked until one of two things happens: Either you find a class that contains the specified method or you don't find the method after going all the way back to the root class. If the first occurs, you're all set; if the second occurs, you have a problem, and a warning message is generated that looks like this: warning: 'ClassB' may not respond to '-inity' In this case, you inadvertently are trying to send a message called inity to a variable of type class ClassB. The compiler told you that variables of that type of class do not know how to respond to such a method. Again, this was determined after checking ClassB's methods and its parents' methods back to the root class (which, in this case, is NSObject).

September 17, 2009

@interface Rectangle: NSObject {     int  width;     int  height; } @property int width, height; -(int)  area; -(int)  perimeter; -(void) setWidth: (int) w andHeight: (int) h;

@end

September 17, 2009

#import "Rectangle.h"

@implementation Rectangle @synthesize width, height; -(void) setWidth: (int) w andHeight: (int) h {     width = w;     height = h; } -(int) area {     return width * height; } -(int) perimeter {     return (width + height) * 2; } @end

September 17, 2009

#import "Rectangle.h" int main (int argc, char *argv[]) {     NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];     Rectangle *myRect = [[Rectangle alloc] init];     [myRect setWidth: 5 andHeight: 8];     NSLog (@"Rectangle: w = %i, h = %i",             myRect.width, myRect.height);     NSLog (@"Area = %i, Perimeter = %i",               [myRect area], [myRect perimeter]);     [myRect release];     [pool drain];     return 0; }

September 17, 2009

Output? Rectangle: w = 5, h = 8 Area = 40, Perimeter = 26

September 17, 2009

#import "Rectangle.h"

@interface Square: Rectangle -(void) setSide: (int) s; -(int) side; @end

September 17, 2009

#import "Square.h"

@implementation Square: Rectangle -(void) setSide: (int) s {     [self setWidth: s andHeight: s]; } -(int) side {    return width; } @end

September 17, 2009

#import "Square.h" #import int main (int argc, char *argv[]) {    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];    Square *mySquare = [[Square alloc] init];    [mySquare setSide: 5];    NSLog (@"Square s = %i", [mySquare side]);    NSLog (@"Area = %i, Perimeter = %i",        [mySquare area], [mySquare perimeter]);    [mySquare release];    [pool drain];    return 0; }

September 17, 2009

Output? Square s = 5 Area = 25, Perimeter = 20

September 17, 2009

#import

@interface XYPoint: NSObject {    int  x;    int  y; } @property int x, y; -(void) setX: (int) xVal andY: (int) yVal; @end

September 17, 2009

#import

@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

September 17, 2009

You used a new directive in the Rectangle.h header file: @class XYPoint; You needed this because the compiler needs to know what an XYPoint is when it encounters it as one of the instance variables defined for a Rectangle. The class name is also used in the argument and return type declarations for your setOrigin: and origin methods, respectively. You do have another choice. You can import the header file instead, like so: #import "XYPoint.h" 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 methods, the @class directive does not suffice because the compiler would need more information; it would need to know how many arguments the method takes, what their types are, and what the method's return type is.

September 17, 2009

#import

@interface XYPoint: NSObject {    int  x;    int  y; } @property int x, y; -(void) setX: (int) xVal andY: (int) yVal; @end

September 17, 2009

#import "XYPoint.h"

@implementation XYPoint @synthesize x, y; -(void) setX: (int) xVal andY: (int) yVal {     x = xVal;     y = yVal; } @end

September 17, 2009

#import

@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

September 17, 2009

#import "Rectangle.h"

@implementation Rectangle @synthesize width, height; -(void) setWidth: (int) w andHeight: (int) h {    width = w;    height = h; } –(void) setOrigin: (XYPoint *) pt {     origin = pt; } –(int) area {     return width * height; } –(int) perimeter {     return (width + height) * 2; } –(XYPoint *) origin {     return origin; } @end

September 17, 2009

#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);    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; }

September 17, 2009

OUTPUT? Rectangle w = 5, h = 8 Origin at (100, 200) Area = 40, Perimeter = 26

September 17, 2009

#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 (@"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; }

September 17, 2009

Now what's the output?

September 17, 2009

Origin at (100, 200) Origin at (50, 50)

Why?

September 17, 2009

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,

September 17, 2009

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.

September 17, 2009

When the origin variable is set to pt inside the method, the pointer stored inside pt is copied into the instance variable origin

September 17, 2009

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 [origin setX: pt.x andY: pt.y]; sets the newly allocated XYPoint to the x, y coordinate of the argument to the method.

September 17, 2009

September 17, 2009

Be sure to now replace: @class directive with #import "XYPoint.h"

Note: we didn't synthesize the origin methods here because the synthesized setter 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.

September 17, 2009

Method Overriding @interface ClassA: NSObject {    int x; } -(void) initVar; @end @implementation ClassA -(void) initVar {     x = 100; } @end

September 17, 2009

@interface ClassB: ClassA -(void) initVar; -(void) printVar; @end @implementation ClassB -(void) initVar    // added method {    x = 200; } -(void) printVar {    NSLog (@"x = %i", x); } @end

September 17, 2009

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; }

Output x=200

September 17, 2009

September 17, 2009

Now let's add a printvar() to ClassA @interface ClassA: NSObject { int x; } -(void) initVar; -(void) printVar; @end @implementation ClassA -(void) initVar { x = 100; } -(void) printVar { NSLog (@"x = %i", x); } @end

September 17, 2009

#import

// 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; }

Output x = 100 x = 200

September 17, 2009

Adding Instance Variables @interface ClassA: NSObject { int x; } -(void) initVar; @end @implementation ClassA -(void) initVar { x = 100; } @end

September 17, 2009

@interface ClassB: ClassA {   int  y; } -(void)  initVar; -(void)  printVar; @end @implementation ClassB -(void) initVar {    x = 200;    y = 300; } -(void) printVar {    NSLog (@"x = %i", x);    NSLog (@"y = %i", y); } @end

September 17, 2009

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; }

Output x = 200 y = 300

September 17, 2009

Abstract Classes

Sometimes classes are created just to make it easier for someone to create a subclass. For that reason, these classes are called abstract classes or, equivalently, abstract superclasses. Methods and instance variables are defined in the class, but no one is expected to actually create an instance from that class. For example, consider the root object NSObject.

September 17, 2009

September 17, 2009

Polymorphism, Dynamic Typing, and Dynamic Binding

Polymorphism enables programs to be developed so that objects from different classes can define methods that share the same name. Dynamic typing defers the determination of the class that an object belongs to until the program is executing. Dynamic binding defers the determination of the actual method to invoke on an object until program execution time.

September 17, 2009

// Interface file for Complex class

#import @interface Complex: NSObject { double real; double imaginary; } @property double real, imaginary; -(void) print; -(void) setReal: (double) a andImaginary: (double) b; -(Complex *) add: (Complex *) f; @end

September 17, 2009

#import "Complex.h" @implementation Complex @synthesize real, imaginary; -(void) print {    NSLog (@" %g + %gi ", real, imaginary); } -(void) setReal: (double) a andImaginary: (double) b {    real = a;    imaginary = b; } -(Complex *) add: (Complex *) f {     Complex *result = [[Complex alloc] init];     [result setReal: real + [f real]             andImaginary: imaginary + [f imaginary]];     return result; } @end

September 17, 2009

#import "Fraction.h" #import "Complex.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

// add and print 2 complex numbers [c1 print]; NSLog (@" +"); [c2 print]; NSLog (@"---------"); compResult = [c1 add: c2]; [compResult print]; NSLog (@"\n");

Fraction *f1 = [[Fraction alloc] init]; Fraction *f2 = [[Fraction alloc] init]; Fraction *fracResult; Complex *c1 = [[Complex alloc] init]; Complex *c2 = [[Complex alloc] init]; Complex *compResult;

[c1 release]; [c2 release]; [compResult release];

[f1 setTo: 1 over: 10]; [f2 setTo: 2 over: 15];

// add and print 2 fractions [f1 print]; NSLog (@" +"); [f2 print]; NSLog (@"----"); fracResult = [f1 add: f2]; [fracResult print];

[c1 setReal: 18.0 andImaginary: 2.5]; [c2 setReal: -5.0 andImaginary: 3.2];

[f1 release]; [f2 release]; [fracResult release]; [pool drain]; return 0; }

September 17, 2009

Output 18 + 2.5i + -5 + 3.2i --------13 + 5.7i 1/10 + 2/15 ---7/30

September 17, 2009

Dynamic Binding

The id data type is a generic object type. That is, id can be used for storing objects that belong to any class. The real power of this data type is exploited when it's used this way to store different types of objects in a variable during the execution of a program.

September 17, 2009

#import "Fraction.h" #import "Complex.h" int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; id dataValue; Fraction *f1 = [[Fraction alloc] init]; Complex *c1 = [[Complex alloc] init]; [f1 setTo: 2 over: 5]; [c1 setReal: 10.0 andImaginary: 2.5]; // first dataValue gets a fraction dataValue = f1; [dataValue print]; // now dataValue gets a complex number dataValue = c1; [dataValue print]; [c1 release]; [f1 release]; [pool drain]; return 0; }

Output 2/5 10 + 2.5i

September 17, 2009

Compile Time Versus Runtime Checking Because the type of object stored inside an id variable can be indeterminate at compile time, some tests are deferred until runtime—that is, while the program is executing. Fraction *f1 = [[Fraction alloc] init]; [f1 setReal: 10.0 andImaginary: 2.5];

VS id dataValue = [[Fraction alloc] init]; ... [dataValue setReal: 10.0 andImaginary: 2.5];

September 17, 2009

The id Data Type and Static Typing If an id data type can be used to store any object, why don't you just declare all your objects as type id? For several reasons, you don't want to get into the habit of overusing this generic class data type. First, when you define a variable to be an object from a particular class, you are using what's known as static typing. The word static refers to the fact that the variable is always used to store objects from the particular class. So the class of the object stored in that type is predeterminate, or static. When you use static typing, the compiler ensures, to the best of its ability, that the variable is used consistently throughout the program. The compiler can check to ensure that a method applied to an object is defined or inherited by that class; if not, it issues a warning message. Thus, when you declare a Rectangle variable called myRect in your program, the compiler checks that any methods you invoke on myRect are defined in the Rectangle class or are inherited from its superclass.

However, if the check is performed for you at runtime anyway, why do you care about static typing? You care because it's better to get your errors out during the compilation phase of your program than during the execution phase. If you leave it until runtime, you might not even be the one running the program when the error occurs. If your program is put into production, some poor unsuspecting user might discover when running the program that a particular object does not recognize a method.

September 17, 2009

September 17, 2009

Methods for Working with Dynamic Types

Method

Question or Action

-(BOOL) isKindOfClass: class-object

Is the object a member of class-object or a descendant?

-(BOOL) isMemberOfClass: class-object

Is the object a member of class-object?

-(BOOL) respondsToSelector: selector

Can the object respond to the method specified by selector?

+(BOOL) instancesRespondToSelector: selector

Can instances of the specified class respond to selector?

+(BOOL) isSubclassOfClass: class-object

Is the object a subclass of the specified class?

-(id) performSelector: selector

Apply the method specified by selector .

-(id) performSelector: selector withObject: object

Apply the method specified by selector , passing the argument object.

-(id) performSelector: selector withObject: object1 withObject: object2

Apply the method specified by selector with the arguments object1 and object2 .

September 17, 2009

int main (int argc, char *argv[]) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

// respondsTo:

Square *mySquare = [[Square alloc] init];

if ( [mySquare respondsToSelector: @selector (setSide:)] == YES ) NSLog (@"mySquare responds to setSide: method");

// isMemberOf:

if ( [mySquare respondsToSelector: @selector (setWidth:andHeight:)] == YES ) NSLog (@"mySquare responds to setWidth:andHeight: method");

if ( [mySquare isMemberOfClass: [Square class]] == YES ) NSLog (@"mySquare is a member of Square class");

if ( [Square respondsToSelector: @selector (alloc)] == YES ) NSLog (@"Square class responds to alloc method");

if ( [mySquare isMemberOfClass: [Rectangle class]] == YES ) NSLog (@"mySquare is a member of Rectangle class");

// instancesRespondTo: if ([Rectangle instancesRespondToSelector: @selector (setSide:)] == YES) NSLog (@"Instances of Rectangle respond to setSide: method");

if ( [mySquare isMemberOfClass: [NSObject class]] == YES ) NSLog (@"mySquare is a member of NSObject class"); // isKindOf:

if ([Square instancesRespondToSelector: @selector (setSide:)] == YES) NSLog (@"Instances of Square respond to setSide: method");

if ( [mySquare isKindOfClass: [Square class]] == YES ) NSLog (@"mySquare is a kind of Square");

if ([Square isSubclassOfClass: [Rectangle class]] == YES) NSLog (@"Square is a subclass of a rectangle");

if ( [mySquare isKindOfClass: [Rectangle class]] == YES ) NSLog (@"mySquare is a kind of Rectangle");

[mySquare release];

if ( [mySquare isKindOfClass: [NSObject class]] == YES ) NSLog (@"mySquare is a kind of NSObject");

[pool drain]; return 0; }

September 17, 2009

Output mySquare is a member of Square class mySquare is a kind of Square mySquare is a kind of Rectangle mySquare is a kind of NSObject mySquare responds to setSide: method mySquare responds to setWidth:andHeight: method Square class responds to alloc method Instances of Square respond to setSide: method Square is a subclass of a rectangle

September 17, 2009

Exception Handling Using @try

#import "Fraction.h"

int main (int argc, char *argv []) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *f = [[Fraction alloc] init]; [f noSuchMethod]; NSLog (@"Execution continues!"); [f release]; [pool drain]; return 0; }

September 17, 2009

#import "Fraction.h"

int main (int argc, char *argv []) { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; Fraction *f = [[Fraction alloc] init]; @try { [f noSuchMethod]; } @catch (NSException *exception) { NSLog(@"Caught %@%@", [exception name], [exception reason]); } NSLog (@"Execution continues!"); [f release]; [pool drain]; return 0; }

September 17, 2009

When the exception occurs, the @catch block gets executed. An NSException object that contains information about the exception gets passed as the argument into this block. As you can see, the name method retrieves the name of the exception, and the reason method gives the reason (which the runtime system also previously printed automatically). After the last statement in the @catch block is executed (we have only one here), the program continues execution with the statement immediately following the block. In this case, we execute an NSLog call to verify that execution has continued and has not been terminated. This is a very simple example to illustrate how to catch exceptions in a program. An @finally block can be used to include code to execute whether or not a statement in a @try block throws an exception. An @throw directive enables you to throw your own exception. You can use it to throw a specific exception, or inside a @catch block to throw the same exception that took you into the block like this: @throw;

September 17, 2009

Lab Time!

September 17, 2009

Modify the convertToNum() in the Fraction class to use exception handling test it!

Related Documents


More Documents from "William Smith"