Vous êtes sur la page 1sur 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]; // will use inherited method


[b printVar]; // reveal value of x;

[b release];

[pool drain];
return 0;
}
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 <Foundation/Foundation.h>

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 <Foundation/Foundation.h>

@interface XYPoint: NSObject


{
   int  x;
   int  y;
}
@property int x, y;

-(void) setX: (int) xVal andY: (int) yVal;


@end
September 17, 2009

#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
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 <Foundation/Foundation.h>

@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 <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
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 <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]; Output -
[b release]; x = 100
x = 200
[pool drain];
return 0;
}
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 <Foundation/Foundation.h>

@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[]) // add and print 2 complex numbers
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; [c1 print]; NSLog (@" +"); [c2 print];
NSLog (@"---------");
compResult = [c1 add: c2];
Fraction *f1 = [[Fraction alloc] init];
[compResult print];
Fraction *f2 = [[Fraction alloc] init];
NSLog (@"\n");
Fraction *fracResult;
Complex *c1 = [[Complex alloc] init];
[c1 release];
Complex *c2 = [[Complex alloc] init];
[c2 release];
Complex *compResult;
[compResult release];

[f1 setTo: 1 over: 10]; // add and print 2 fractions


[f2 setTo: 2 over: 15]; [f1 print]; NSLog (@" +"); [f2 print];
NSLog (@"----");
fracResult = [f1 add: f2];
[c1 setReal: 18.0 andImaginary: 2.5]; [fracResult print];
[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];
Output -
// now dataValue gets a complex number

dataValue = c1;
[dataValue print];
2/5
[c1 release];
10 + 2.5i
[f1 release];

[pool drain];
return 0;
}
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: Is the object a member of class-object?


class-object

-(BOOL) respondsToSelector: Can the object respond to the method specified


selector by selector?

+(BOOL) instancesRespondToSelector: Can instances of the specified class respond to


selector selector?

+(BOOL) isSubclassOfClass: Is the object a subclass of the specified class?


class-object

-(id) performSelector: selector Apply the method specified by selector .

-(id) performSelector: Apply the method specified by selector ,


selector withObject: object passing the argument object.

-(id) performSelector: Apply the method specified by selector with


selector withObject: object1 the arguments object1 and object2 .
withObject: object2
September 17, 2009

int main (int argc, char *argv[])


{ // respondsTo:
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if ( [mySquare respondsToSelector: @selector (setSide:)] == YES )
Square *mySquare = [[Square alloc] init]; NSLog (@"mySquare responds to setSide: method");

if ( [mySquare respondsToSelector: @selector (setWidth:andHeight:)] == YES )


// isMemberOf: NSLog (@"mySquare responds to setWidth:andHeight: method");

if ( [mySquare isMemberOfClass: [Square class]] == YES ) if ( [Square respondsToSelector: @selector (alloc)] == YES )
NSLog (@"mySquare is a member of Square class"); NSLog (@"Square class responds to alloc method");

if ( [mySquare isMemberOfClass: [Rectangle class]] == YES ) // instancesRespondTo:


NSLog (@"mySquare is a member of Rectangle class");
if ([Rectangle instancesRespondToSelector: @selector (setSide:)] == YES)
if ( [mySquare isMemberOfClass: [NSObject class]] == YES ) NSLog (@"Instances of Rectangle respond to setSide: method");
NSLog (@"mySquare is a member of NSObject class");
if ([Square instancesRespondToSelector: @selector (setSide:)] == YES)
// isKindOf: NSLog (@"Instances of Square respond to setSide: method");

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

if ( [mySquare isKindOfClass: [Rectangle class]] == YES ) [mySquare release];


NSLog (@"mySquare is a kind of Rectangle");
[pool drain];
if ( [mySquare isKindOfClass: [NSObject class]] == YES ) return 0;
NSLog (@"mySquare is a kind of NSObject"); }
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!

Vous aimerez peut-être aussi