02.06.2008

Vererbung und Polymorphie

In dem Artikel geht des um Vererbung und Polymorphie. Um zu zeigen wie das Prinzip der Polymorphie ist, werden drei Klasse definiert die alle von der Klasse JHGraphic erben und die Funktion draw implementieren.

Das folgende UML-Digramm zeigt die Vererbungsstruktur:

Die Header-Datei für JHGraphic definiert nur die Funktion draw.

			
@interface JHGraphic : NSObject {
}
- (void)draw;
@end 
 
Die dazugehörige Klasse ist leer:
 
@implementation JHGraphic
- (void)draw {
	NSLog(@"Draw JHGraphic");
}
@end

Die drei Klassen die von JHGraphic erben, implementieren die Funktion draw.

Die Klasse JHLine sieht folgendermaßen aus:

@implementation JHLine
- (void)draw {
	NSLog(@"Draw JHLine");
}
@end 

Die Klasse JHText sieht auch nicht viel anders auch:

@implementation JHText
- (void)draw {
	NSLog(@"Draw JHText");
}
@end 

Und abschließend die Klasse JHRectangle:

@implementation JHRectangle
- (void)draw {
	NSLog(@"Draw JHRectangle");
}
@end 

Die Header-Dateien zu den Klassen enthalten keine weiteren Attribute oder Funktionen und sind deshalb hier nicht aufgeführt.

Das folgende Programm benutzt die Klassen JHLine, JHText und JHRectangle:

#import <Foundation/Foundation.h>
#import "JHGraphic.h"
#import "JHLine.h"
#import "JHText.h"
#import "JHRectangle.h"
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
  
	JHGraphic* graphic;
	graphic = [[JHLine alloc] init];
	[graphic draw];
 
	graphic = [[JHText alloc]init];
	[graphic draw];
	
	graphic = [[JHRectangle alloc]init];
	[graphic draw];
 
    [pool drain];
    return 0;
} 

Obwohl die Funktion draw immer auf der Klasse JHGraphic aufgerufen wird, erfolgt die richtige Ausgabe. D.h. der Aufruf wird an die passende Klasse weitergeleitet.

Die Ausgabe des Programms sieht so aus:

2008-06-02 22:24:20.393 Vererbung[42185:10b] Draw JHLine
2008-06-02 22:24:20.395 Vererbung[42185:10b] Draw JHText
2008-06-02 22:24:20.396 Vererbung[42185:10b] Draw JHRectangle 

02.06.2008

Erweitern von Klassen mit Vererbung

In diesem Artikel werden folgende Themen behandelt:

  • Vererbung
  • Funktionen mit unterschiedlichen Parametertypen
  • Initialisierung (init) von Objekten
  • Allokierung (alloc) und Deallokierung (dealloc) von Speicher

Das folgende UML-Diagramm zeigt eine Klasse JHGraphic, die eine Funktion draw definiert. Alle anderen Klassen erben von dieser Klasse und erweitern die Klasse um verschiedene Funktionen:

Die Header-Datei von JHGraphic sehen folgendermaßen aus:

#import <Cocoa/Cocoa.h>
 
@interface JHGraphic : NSObject {
}
 
- (void)draw;
@end

Die JHGraphic Klasse ist im Folgenden zu sehen. Dabei ist zu beachten, daß sie eine Funktion init enthält, die bei der Initialisierung aufgerufen wird. Diese Funktion macht noch nichts spannendes. Sie leitet den init-Aufruf einfach weiter an NSObject. Außerdem sieht man die Funktion dealloc. Sie sorgt dafür, daß der Speicher wieder freigegeben wird. In dem Beispiel leitet sie den Aufruf nur weiter.

@implementation JHGraphic
 
- (JHGraphic*)init {
	NSLog(@">> Init JHGraphic");
	return [super init];;
}
 
- (void)draw {
	NSLog(@"Draw JHGraphic");
}
 
- (void) dealloc {
	NSLog(@">> Dealloc JHGraphic");
	[super dealloc];
}
@end 

Die Header-Datei der Klasse JHText sieht folgendermaßen aus:

@interface JHText : JHGraphic {
	NSString* text;
}
 
- (NSString*)text;
- (void)setText:(NSString*)newText;
@end 

Die Implementierung dazu:

@implementation JHText
 
- (JHText*)init {
	NSLog(@">> Init JHText");
	self = [super init];
	if(self) {
		[self setText:[NSString string]];
	}
	return self;
} 
 
- (void)draw {
	NSLog(@"Draw JHText");
}
 
- (NSString*)text {
	return text;
}
 
- (void)setText:(NSString*)newText {
 	if(newText != text) {
		[text release];
		text = [newText retain];
	}
}
 
- (void) dealloc {
	NSLog(@">> Dealloc JHText");
	[self setText:nil];
	[super dealloc];
}
@end 

Besonderheit hier ist, daß die Funktion setText als Paramenter ein Objekt vom Typ NSString übergeben bekommt. Außerdem wird in der Funktion init ein NSString erzeugt und der Variable text zugewiesen. Die Funktion dealloc sorgt dafür, daß der Speicher von text wieder freigegeben werden kann.

Die Header-Datei zu JHRectangle sieht so aus:

@interface JHRectangle : JHGraphic {
	int height;
	int width;
}
 
- (void)setHeight:(int)newHeight;
- (int)height;
 
- (void)setWidth:(int)newWidth;
- (int)width;
 
@end 

Die Implemetierung so:

@implementation JHRectangle
- (JHRectangle*)init {
	NSLog(@">> Init JHRectangle");
	self = [super init];
	if(self) {
		[self setHeight:0];
		[self setWidth:0];
	}
	return self;
}
 
- (void)draw {
	NSLog(@"Draw JHRectangle");
}
 
- (void)setHeight:(int)newHeight {
	height = newHeight;
}
 
- (int)height {
	return height;
}
 
- (void)setWidth:(int)newWidth {
	width = newWidth;
}
 
- (int)width {
	return width;
}
 
- (void) dealloc {
	NSLog(@">> Dealloc JHRectangle");
	[super dealloc];
}
@end 

Die Besonderheit an den Funktionen dieser Klasse ist, daß die Parameter einfache Datentypen sind. Hier also ints. In der init-Funktion werden die Variablen height und width mit 0 initialisiert. Die dealloc-Funktion sorgt wieder für das Aufräumen.

Die Header-Datei von JHLine sieht so aus:

@interface JHLine : JHGraphic {
	NSNumber* length;
}
 
- (void)setLength:(NSNumber*)newLength;
- (NSNumber*)length;
 
@end 

Die Implementierung dazu sieht man hier:

@implementation JHLine
- (JHLine*)init {
	NSLog(@">> Init JHLine");
	self = [super init];
	if(self) {
		[self setLength:[NSNumber numberWithInt:0]];
	}
	return self;
}
 
- (void)draw {
	NSLog(@"Draw JHLine");
}
 
- (void)setLength:(NSNumber*)newLength {
 	if(newLength != length) {
		[length release];
		length = [newLength retain];
	}
}
 
- (NSNumber*)length {
	return length;
}
 
- (void) dealloc {
	NSLog(@">> Dealloc JHLine");
	[self setLength:nil];
	[super dealloc];
}
@end 

In dieser Klasse wird als Parameter ein Objekt vom Typ NSNumber übergeben. In der init-Methode wird, wie gehabt, ein Objekt von Typ NSNumber erzeugt und der Variable length zugewiesen. Die Funktion dealloc räumt wieder auf.

Das Hauptprogramm, daß die Klassen benutzt kann so aufgebaut sein:

#import <Foundation/Foundation.h>
#import "JHGraphic.h"
#import "JHLine.h"
#import "JHText.h"
#import "JHRectangle.h"
 
int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
 
	JHText* text;
	text = [[JHText alloc]init];
	[text setText:@"Hello World!"];
	NSLog(@"Text:%@", [text text]);
	[text release];	
 
	JHRectangle* rectangle;
	rectangle = [[JHRectangle alloc]init];
	[rectangle setHeight:10];
	[rectangle setWidth:3];
	NSLog(@"Height:%i Width:%i", [rectangle height], [rectangle width]);
	[rectangle release]; 
 
	JHLine* line;
	line = [[JHLine alloc] init];
	NSNumber* newLength;
	newLength = [[NSNumber alloc]initWithInt:13];
	[line setLength:newLength];
	NSLog(@"Length:%i", [[line length] intValue]);
	[line release]; 
 
    [pool drain];
    return 0;
} 

Die Initialisierung und Allokierung ist bei allen Klasse gleich. Zu beachten ist, daß bei der Initialisierung jeweils die init-Funktion der zu initalisierenden Klasse aufgerufen wird. Also die Funktion die in der Klasse überschrieben wurde.

Bei JHText wird die Funktion setText mit dem Parameter "Hello World!" aufgerufen. In der nächsten Zeile, wird der getter text verwendet und den gesetzten Wert wieder auszulesen.

Bei JHRectangle verhält es sich ähnlich, nur daß als Parameter die Werte 10 und 3 übergeben werden. Die Ausgabe der gesetzten Werte erfolgt über die getter height und width.

Bei JHLine muß als Parameter ein Objekt von Typ NSNumber übergeben werden. Dieses muß natürlich vorher erzeugt und mit einem Wert initialisiert werden. Dies geschieht mit der Funktion initWithInt und dem Wert 13. Zum Setzen wird der setter setLength verwendet. Bei der Ausgabe wird der getter length verwendet. Da der Rückgabewert der Funktion ein Objekt vom Typ NSNumber ist, muß noch die Funktion intValue aufgerufen werden um ein int zu erhalten.

Nach der Benutzung der Klassen JHText, JHRectangle und JHLine wird jeweils ein release an das Objekt geschickt. Dies bewirkt, daß die Funktion dealloc in der Klasse aufgerufen wird und den Speicher freigibt.

Die Ausgabe des Programms sieht folgendermaßen aus:

2008-06-05 09:53:51.591 Vererbung[44814:10b] >> Init JHText
2008-06-05 09:53:51.592 Vererbung[44814:10b] >> Init JHGraphic
2008-06-05 09:53:51.593 Vererbung[44814:10b] Text:Hello World!
2008-06-05 09:53:51.594 Vererbung[44814:10b] >> Dealloc JHText
2008-06-05 09:53:51.594 Vererbung[44814:10b] >> Dealloc JHGraphic
2008-06-05 09:53:51.595 Vererbung[44814:10b] >> Init JHRectangle
2008-06-05 09:53:51.595 Vererbung[44814:10b] >> Init JHGraphic
2008-06-05 09:53:51.597 Vererbung[44814:10b] Height:10 Width:3
2008-06-05 09:53:51.597 Vererbung[44814:10b] >> Dealloc JHRectangle
2008-06-05 09:53:51.598 Vererbung[44814:10b] >> Dealloc JHGraphic
2008-06-05 09:53:51.598 Vererbung[44814:10b] >> Init JHLine
2008-06-05 09:53:51.598 Vererbung[44814:10b] >> Init JHGraphic
2008-06-05 09:53:51.599 Vererbung[44814:10b] Length:13
2008-06-05 09:53:51.599 Vererbung[44814:10b] >> Dealloc JHLine
2008-06-05 09:53:51.599 Vererbung[44814:10b] >> Dealloc JHGraphic

Es ist genau zu sehen, wie die Funktionen init und dealloc bei der Initialisierung und bei der Deallokierung des jeweiligen Objekts aufgerufen werden.