Tuesday 23 June 2009

Chapter 9 - Polymorphism, Dynamic Typing and Dynamic Binding

Comment

I think I understand the fundamentals behind 'polymorphism'. To summarise, the main advantage lies from being able to invoke a method without worrying what class that method derives from. Example being you can use 'dynamic typing' in order to invoke a method on a generic object type, at runtime 'dynamic binding' will dictate whether the method it uses is from ClassA or ClassB depending whether the data held in the generic object type is of ClassA or ClassB respectively.


Exercises

1. What will happen if you insert the message expression

[compResult reduce];

into Program 9.1 after the addition is performed (but before compResult is released)? Try it and see.


ANS - Nothing will happen since 'reduce' is not a method of the Complex class and the object 'compResult' has been defined as being an object of the Complex class which contains no such method.


Tested program and its console output.


#import

#import "Complex.h"


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

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


Complex *c1 = [[Complex alloc] init];

Complex *c2 = [[Complex alloc] init];

Complex *compResult;

[c1 setReal: 18.0 andImaginary: 2.5];

[c2 setReal: -5.0 andImaginary: 3.2];

//add and print two complex numbers

[c1 print]; NSLog (@" +"); [c2 print];

NSLog (@"---------");

compResult = [c1 add: c2];

[compResult print];

NSLog (@"\n");

[compResult reduce];

[c1 release];

[c2 release];

[compResult release];

[pool drain];

return 0;

}


[Session started at 2009-06-23 22:15:38 +0100.]

2009-06-23 22:15:38.112 Ex1[394:10b] 18 + 2.5i

2009-06-23 22:15:38.114 Ex1[394:10b] +

2009-06-23 22:15:38.114 Ex1[394:10b] -5 + 3.2i

2009-06-23 22:15:38.115 Ex1[394:10b] ---------

2009-06-23 22:15:38.115 Ex1[394:10b] 13 + 5.7i

2009-06-23 22:15:38.115 Ex1[394:10b]

2009-06-23 22:15:38.116 Ex1[394:10b] *** -[Complex reduce]: unrecognized selector sent to instance 0x104c90

2009-06-23 22:15:38.117 Ex1[394:10b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[Complex reduce]: unrecognized selector sent to instance 0x104c90'


Literally translates to: 'major crash related to an unrecognised method call (or selector) being sent to a specific area of memory.


2. Can the id variable dataValue, as defined in Program 9.2, be assigned a Rectangle object as you defined it in Chapter 8? That is, is the statement

dataValue = [[Rectangle alloc] init];

valid? Why or why not?


ANS - It is valid as although it is a generic object type, it is still able to inherit the attributes of other classified object. The advantage being it can primarily act as a Rectangle object but then be reclassified as a Complex object.


3. Add a print method to your XYPoint class defined in Chapter 8. Have it display the point in the format (x, y). Then modify Program 9.2 to incorporate an XYPoint object. Have the modified program create an XYPoint object, set its value, assign it to the id variable dataValue, and then display its value.


ANS - Copy and Paste of added XYPoint method and modified Program 9.2 along with its console output.


// XYPoint.m

-(void) print

{

NSLog (@"point (%i, %i)", x, y);

}


// main program

#import

#import "XYPoint.h"


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

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


id dataValue;

XYPoint *point = [[XYPoint alloc] init];

[point setX: 5 andY: 8];

dataValue = point;

[dataValue print];

[point release];

[pool drain];

return 0;

}


[Session started at 2009-06-23 13:50:14 +0100.]

2009-06-23 13:50:14.673 Ex2[475:10b] point (5, 8)


The Debugger has exited with status 0.


4. Based on the discussions about argument and return types in this chapter, modify both add: methods in the Fraction and Complex classes to take and return id objects. Then write a program that incorporates the following code sequence:

result = [dataValue1 add: dataValue2];

[result print];

Here, result, dataValue1 and dataValue2 are id objects. Make sure you set dataValue1 and dataValue2 appropriately in your program and release all objects before your program terminates.


ANS - Modified methods made to both class interface and implementation files. Copy and Paste of main program and its console output. Couldn't get this one to compile due to syntax errors.


#import

#import "Fraction.h"

#import "Complex.h"


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

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


Fraction *aFrac = [[Fraction alloc] init];

Complex *aComp = [[Fraction alloc] init];

id result;

id dataValue1;

id dataValue2;

[aFrac setNumerator: 1 andDenominator: 4];

dataValue1 = aFrac;

[aFrac setNumerator: 1 andDenominator: 2];

dataValue2 = aFrac;

[aFrac release];

result = [dataValue1 add: dataValue2];

[result print];

[aComp setReal: 3.0 andImaginary: 1.5];

dataValue1 = aComp;

[aComp setReal: 10.0 andImaginary: 4.5];

dataValue2 = aComp

[aComp release];

result = [dataValue1 add: dataValue2];

[result print];

[result release];

[dataValue1 release];

[dataValue2 release];

[pool drain];

return 0;

}


5. Given the Fraction and Complex class definitions you have been using in this text and the following definitions


Fraction *fraction = [[Fraction alloc] init];

Complex *complex = [[Complex alloc] init];

id number = [[Complex alloc] init];


determine the return value from the following message xpressions. Then type them into a program to verify the results.


[fraction isMemberOfClass: [Complex class]];

[complex isMemberOfClass: [NSObject class]];

[complex isKindOfClass: [NSObject class]];

[fraction isKindOfClass: [Fraction class]];

[fraction respondsToSelector: @selector (print)];

[complex respondsToSelector: @selector (print)];

[Fraction instancesRespondToSelector: @selector (print)];

[number respondsToSelector: @selector (print)];

[number isKindOfClass: [Complex class]];

[number respondsToSelector: @selector (release)];

[[number class] respondsToSelector: @selector (alloc)];


ANS - Expected output and console output


Expected Output

NO - fraction is an instance of the Fraction class

NO - complex is an instance of the Complex class and not the NSObject class

YES

YES

YES

YES

YES

YES

YES

YES

NO - because number is not a class, it is an object belonging to a class



[Session started at 2009-06-23 23:17:18 +0100.]

2009-06-23 23:17:18.119 Ex5[746:10b] NO

2009-06-23 23:17:18.121 Ex5[746:10b] NO

2009-06-23 23:17:18.121 Ex5[746:10b] YES

2009-06-23 23:17:18.121 Ex5[746:10b] YES

2009-06-23 23:17:18.122 Ex5[746:10b] YES

2009-06-23 23:17:18.122 Ex5[746:10b] YES

2009-06-23 23:17:18.122 Ex5[746:10b] YES

2009-06-23 23:17:18.123 Ex5[746:10b] YES

2009-06-23 23:17:18.123 Ex5[746:10b] YES

2009-06-23 23:17:18.123 Ex5[746:10b] YES

2009-06-23 23:17:18.124 Ex5[746:10b] YES


The Debugger has exited with status 0.


6. Modify the Calculator class you developed in the exercise from Chapter 4 so that division is done in an @try block. If the division throws an exception, log an error message and continue program execution.


ANS - Copy and Paste of all parts of program including console output. Program compiles but receive awkward console output.


//

// Calculator.h

// Ex6

//

// Created by Waqas Arshid on 23/06/2009.

// Copyright 2009 __MyCompanyName__. All rights reserved.

//


#import



@interface Calculator : NSObject

{

double accumulator;

double memory;

}


@property double accumulator;


-(void) clear;


-(void) divide: (double) value;


@end


//

// Calculator.m

// Ex6

//

// Created by Waqas Arshid on 23/06/2009.

// Copyright 2009 __MyCompanyName__. All rights reserved.

//


#import "Calculator.h"



@implementation Calculator


@synthesize accumulator;


-(void) clear

{

accumulator = 0;

}


-(void) divide: (double) value

{

@try {

accumulator /= value;

NSLog (@"The resulting value of accumulator is: %f", accumulator);

}

@catch (NSException *exception) {

NSLog (@"Caught division by zero - error.");

}

}


@end


// main


#import

#import "Calculator.h"


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

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


Calculator *deskCalc = [[Calculator alloc] init];

[deskCalc clear];

[deskCalc setAccumulator: 100.0];

[deskCalc divide: 0.0];

NSLog (@"The final result is: %f", [deskCalc accumulator]);

[deskCalc release];

[pool drain];

return 0;

}


[Session started at 2009-06-23 23:41:13 +0100.]

2009-06-23 23:41:13.132 Ex6[842:10b] The resulting value of accumulator is: inf

2009-06-23 23:41:13.133 Ex6[842:10b] The final result is: inf


The Debugger has exited with status 0.


Friday 12 June 2009

Chapter 8 - Inheritance

Comment

I found the concepts in this chapter relatively straight forward. However the later exercises for this chapter appear to be quite challenging but hopefully they aren't that hard.


EDIT: The formatting of several areas of this post, didn't exactly turn out the way I'd hoped hopefully it doesn't make it too difficult to read.


Exercises


1. Add a new class called ClassC, which is a subclass of ClassB, to Program 8.1. Make an initVar method that sets the value of its instance variable x to 300. Write a test routine that declares ClassA, ClassB, ClassC objects and invokes their corresponding initVar methods.


ANS - Copy and Paste of code and its console output


#import


//ClassA


@interface ClassA: NSObject

{

int x;

}


-(void) initVar;

-(void) printVar;

@end


@implementation ClassA

-(void) initVar

{

x = 100;

}


-(void) printVar

{

NSLog(@"x = %i", x);

}


@end


//ClassB


@interface ClassB : ClassA

-(void) initVar;

@end


@implementation ClassB

-(void) initVar

{

x = 200;

}

@end


//ClassC


@interface ClassC : ClassB

-(void) initVar;

@end


@implementation ClassC

-(void) initVar

{

x = 300;

}

@end


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

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


ClassA *a = [[ClassA alloc] init];

ClassB *b = [[ClassB alloc] init];

ClassC *c = [[ClassC alloc] init];

[a initVar];

[b initVar];

[c initVar];

[a printVar];

[b printVar];

[c printVar];

[a release];

[b release];

[c release];

[pool drain];

return 0;

}


[Session started at 2009-06-03 09:33:32 +0100.]

2009-06-03 09:33:32.788 Chap8_Ex1[282:10b] x = 100

2009-06-03 09:33:32.790 Chap8_Ex1[282:10b] x = 200

2009-06-03 09:33:32.790 Chap8_Ex1[282:10b] x = 300


The Debugger has exited with status 0.


2. When dealing with higher-resolition 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 Recatangle classes from this chapter to deal with floating-point numbers. The rectangle's width, height, area, and perimeter should all work with floating-point numbers as well.


ANS - Code for modified Rectangle and XYPoint header and implementation files


//

// Rectangle.h

// Ex2

//

// Created by Waqas Arshid on 03/06/2009.

// Copyright 2009 __MyCompanyName__. All rights reserved.

//


#import


@class XYPoint

@interface Rectangle : NSObject

{

float width;

float height;

XYPoint *origin;

}


@property float width, height;


-(XYPoint *) origin;

-(void) setOrigin: (XYPoint *) pt;

-(void) setWidth: (float) w andHeight: (float) h;

-(float) area;

-(float) perimeter;


@end


//

// Rectangle.m

// Ex2

//

// Created by Waqas Arshid on 03/06/2009.

// Copyright 2009 __MyCompanyName__. All rights reserved.

//


#import "Rectangle.h"

#import "XYPoint.h"



@implementation Rectangle


@synthesize width, height;


-(void) setWidth: (float) w andHeight: (float) h

{

width = w;

height = h;

}


-(float) width

{

return width * height;

}


-(float) perimeter

{

return (width + height) * 2;

}


-(void) setOrigin: (XYPoint *) pt

{

origin = pt;

}


-(XYPoint *) origin

{

return origin;

}

@end


//

// XYPoint.h

// Ex2

//

// Created by Waqas Arshid on 03/06/2009.

// Copyright 2009 __MyCompanyName__. All rights reserved.

//


#import



@interface XYPoint : NSObject

{

float x;

float y;

}


@property float x, y;


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

@end


//

// XYPoint.m

// Ex2

//

// Created by Waqas Arshid on 03/06/2009.

// Copyright 2009 __MyCompanyName__. All rights reserved.

//


#import "XYPoint.h"



@implementation XYPoint


@synthesize x, y;

-(void) setX: (float) xVal andY: (float) yVal

{

x = xVal;

y = yVal;

}

@end



3. Modify the Program 8.1 to add a new class called ClassB2 that, like ClassB, is a subclass of ClassA.

What can you say about the relationship between ClassB and ClassB2?

Identify the hierarchical relationship between the Object class, ClassA, ClassB and ClassB2.

What is the superclass of ClassB?

What is the superclass of ClassB2?

How many subclasses can a class have, and how many superclasses can it have?


ANS - ClassB and ClassB2 are sibling classes, they both share the same parent class.


Object

|

|

|

ClassA

| |

| |

| |

ClassB ClassB2

|

|

|

ClassC


The superclass of ClassB is ClassA

The superclass of ClassB2 is ClassA


A class can have an infinite number of subclasses.

A class can only have one superclass.


4. Write a Rectangle method called translate: that takes a vector called XYPoint (Xv, Yv) as its argument. Have it translate the rectangle's origin by the specified vector.


ANS - I don't understand this question. Is it intending on distinguishing the origin of a rectangle from a co-ordinate on the rectangle and existing parameters such as width and height?


5. Define a new class called GraphicObject, and make it a subclass of NSObject. Define instance variables in your new class as follows:


int fillColour; // 32-bit colour

BOOL filled; // Is the object filled?

int lineColour; // 32-bit line colour?


Write methods to set and retrieve the variables defined previously.

Make the Rectangle class a subclass of GraphicObject

Define new classes, Circle and Triangle, which are also subclasses of GraphicObject. Write methods to set and retrieve the various parameters for these objects and also to calculate the the circle's circumference and area, and the triangle's perimeter and area.


ANS - Copy and Paste of all interface/implementation with a main program and console output to test the new classes and methods


#import


//GraphicObject


@interface GraphicObject : NSObject

{

int fillColour;

BOOL filled;

int lineColour;

}


@property int fillColour, lineColour;

-(void) setFilled;

-(BOOL) filled;


@end


@implementation GraphicObject


@synthesize fillColour, lineColour;

-(void) setFilled

{

filled = TRUE;

}


-(BOOL) filled;

{

return filled;

}


@end


//Rectangle


@interface Rectangle : GraphicObject

{

int width;

int height;

}


@property int width, height;


-(void) setWidth: (int) w andHeight: (int) h;


@end


@implementation Rectangle


@synthesize width, height;


-(void) setWidth: (int) w andHeight: (int) h

{

width = w;

height = h;

}


@end


//Circle


@interface Circle : GraphicObject

{

int radius;

}


@property int radius;


-(float) circumference;

-(float) area;


@end


@implementation Circle


@synthesize radius;


-(float) circumference

{

return 2 * 3.14 * radius; // circumference equals '2 pi r'

}


-(float) area

{

return 3.14 * ( radius * radius ); //area equals 'pi r squared'

}


@end


//Triangle


@interface Triangle : GraphicObject

{

int base;

int height;

float perpHeight;

}


@property int base, height;

@property float perpHeight;


-(int) perimeter;

-(float) area;

-(void) setBase: (int) b andHeight: (int) h;


@end


@implementation Triangle


@synthesize base, height;

@synthesize perpHeight;


-(int) perimeter

{

return base + height + height;

}


-(float) area

{

perpHeight = ( height / 2 ) * 3;

return 0.5 * base * perpHeight;

}


-(void) setBase: (int) b andHeight: (int) h

{

base = b;

height = h;

}


@end


//main


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

NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];


Rectangle *aRectangle = [[Rectangle alloc] init];

Triangle *aTriangle = [[Triangle alloc] init];

Circle *aCircle = [[Circle alloc] init];

[aRectangle setWidth: 5 andHeight: 3];

[aTriangle setBase: 5 andHeight: 3];

[aCircle setRadius: 5];

NSLog (@"Rectangle width is: %i height is: %i", aRectangle.width, aRectangle.height);

NSLog (@"Triangle base is: %i height is: %i", aTriangle.base, aTriangle.height);

NSLog (@"Circle radius is: %i", aCircle.radius);

NSLog (@"Circle circumference is: %f area is: %f", aCircle.circumference, aCircle.area);

NSLog (@"Triangle perimeter is: %i area is: %f", aTriangle.perimeter, aTriangle.area);

[aRectangle release];

[aTriangle release];

[aCircle release];

[pool drain];

return 0;

}


[Session started at 2009-06-12 00:52:52 +0100.]

2009-06-12 00:52:52.629 Ex5[258:10b] Rectangle width is: 5 height is: 3

2009-06-12 00:52:52.631 Ex5[258:10b] Triangle base is: 5 height is: 3

2009-06-12 00:52:52.631 Ex5[258:10b] Circle radius is: 5

2009-06-12 00:52:52.632 Ex5[258:10b] Circle circumference is: 31.400000 area is: 78.500000

2009-06-12 00:52:52.632 Ex5[258:10b] Triangle perimeter is: 11 area is: 7.500000


The Debugger has exited with status 0.


6. Write a Rectangle method called intersect: that takes a rectangle as an argument and returns a rectangle representing the overlapping area between the two rectangles. For example, given two rectangles of dimensions (250 width x 75 height) and (100 width x 180 height), the method should return a rectangle whose origin is at (400, 420), whose width is 50, and whose height is 60.


If the rectangles do not intersect, return one whose width and height are zero and whose origin is at (0, 0).


ANS - @d. I couldn't work this one out completely without a few complications, will bring in code.


7. Write a method for the Rectangle class called draw that draws a rectangle using dashes and vertical bar characters. The following code sequence


Rectangle *myRect = [[Rectangle alloc] init];

[myRect setWidth: 10 and Height: 3];

[myRect draw];

[myRect release];


would produce the following output:


_____


| |

| |

| |

_____


ANS - Copy and Paste of new method and test program with console output. I couldn't get this to work either. Will bring it in also. @d. This is the modified version of the method draw from what you told me about assigning the number of lines/spaces to a string variable and displaying it at the end. However this didn't seem to work also, I am unsure as to why that is.


-(void) draw

{

int n, count;

char space, line;

for ( n = 1; n <= width; ++n ) {

line += "_";

}

NSLog (@"%c", line);

n = 0;

for ( n = 1; n <= height; ++n ) {

NSLog (@"|");

for ( count = 1; n <= (width - 2); ++count ) {

space += " ";

}

NSLog (@"%c", space);

NSLog (@"|");

}

n = 0;

for ( int n = 1; n <= width; ++n ) {

NSLog (@"_");

}

}