NSNumber
Da primitive C-Datentypen wie float, int oder double nicht ein NSArray oder NSDictionary hinzugefügt werden können, gibt es im Foundation Framework Klassen, die für dieses Problem Abhilfe schafften. Eine dieser Klassen ist NSNumber, eine Wrapper-Klasse, die speziell für Zahlen konzipiert wurde.
Als Wrapper bezeichnet man in der Programmierung eine Schnittstelle im Programmcode, in diesem Beispiel eine Klasse, die es ermöglicht, einen primitiven Datentyp als Objekt zu behandeln. Wrapper werden aber auch eingesetzt, um hardwarenahe Systemfunktionen auf eine besser verständliche Klasse abzubilden, oder um Zugriff auf kritische Funktionen einzuschränken.
In der Anwendung erweist sich NSNumber als besonders vielseitig, funktioniert diese Klasse doch als Container für vielerlei primitive Datentypen, wie int, float, double und sogar bool.
Da primitive C-Datentypen wie float, int oder double nicht ein NSArray oder NSDictionary hinzugefügt werden können, gibt es im Foundation Framework Klassen, die für dieses Problem Abhilfe schafften. Eine dieser Klassen ist NSNumber, eine Wrapper-Klasse, die speziell für Zahlen konzipiert wurde.
Als Wrapper bezeichnet man in der Programmierung eine Schnittstelle im Programmcode, in diesem Beispiel eine Klasse, die es ermöglicht, einen primitiven Datentyp als Objekt zu behandeln. Wrapper werden aber auch eingesetzt, um hardwarenahe Systemfunktionen auf eine besser verständliche Klasse abzubilden, oder um Zugriff auf kritische Funktionen einzuschränken.
In der Anwendung erweist sich NSNumber als besonders vielseitig, funktioniert diese Klasse doch als Container für vielerlei primitive Datentypen, wie int, float, double und sogar bool.
|
double
myDouble = 12.14; NSNumber *myNumber = [[NSNumber alloc] initWithDouble:myDouble]; NSLog(@"Number: %@",myNumber); |
Besonderes Augenmerk sollte man jetzt auf den Format Specifier legen. Die gespeicherte Zahl ist jetzt ein Objekt und muss durch %@ ausgegeben werden. Was aber wird in diesem Bezeichner gespeichert? Ist NSNumber nur ein Speicherzeiger auf den tatsächlichen double Wert? Wäre dies so, könnte sich der double-Wert verändern, wenn das Objekt verändert wird. Zu dieser Ansicht könnte man vielleicht kommen. Ein paar Zeilen Programmcode liefern Gewissheit, was tatsächlich geschieht.
|
double
myDouble = 12.14; NSNumber *myNumber = [[NSNumber alloc] initWithDouble:myDouble]; NSLog(@"Number: %@",myNumber); myNumber = [NSNumber numberWithDouble:55.44]; NSLog(@"Number: %@",myNumber); NSLog(@"double: %f",myDouble); |
Natürlich gibt es keine Wechselwirkung mit myDouble. Auch die alternative, verkürzte Schreibwiese macht da keinen Unterschied.
| NSNumber *myNumber = [NSNumber numberWithDouble:myDouble]; |
Diese Schreibweise nennt man auch convenience Allocator. Diese Anweisungen ist nicht nur leichter zu lesen, sondern auf das Objekt wird auch automatisch ein autorelease ausgeführt. Ein explizites release des Objektes ist deshalb nicht mehr nötig.
Wie der Code beweist, gibt es keine permanente Verbindung zwischen NSNumber und dem ursprünglichen double-Wert.
Wie aber verhält es sich, wenn NSNumber in einem Array ist? Da es ein Objekttyp ist, kann myNumber einem NSMutableArray hinzugefügt werden. Das ist der große Vorteil gegenüber einem primitiven double-Wert.
Wie der Code beweist, gibt es keine permanente Verbindung zwischen NSNumber und dem ursprünglichen double-Wert.
Wie aber verhält es sich, wenn NSNumber in einem Array ist? Da es ein Objekttyp ist, kann myNumber einem NSMutableArray hinzugefügt werden. Das ist der große Vorteil gegenüber einem primitiven double-Wert.
|
NSMutableArray
*myArray = [[NSMutableArray alloc] init]; NSNumber *myNumber = [NSNumber numberWithDouble:752.65]; [myArray addObject:myNumber]; myNumber = [NSNumber numberWithDouble:55.44]; NSLog(@"Number im Array: %@",[myArray objectAtIndex:0]); NSLog(@"Number: %@",myNumber); |
Auch in diesem Fall reflektiert die Änderung von myNumber nicht das Objekt im Array. Die Erklärung für dieses Verhalten ist denkbar einfach. NSNumber ist immutable, das heisst, nicht änderbar. Was scheinbar als Überschreiben des bestehenden Wertes aussieht, ist es in Wirklichkeit nicht. Bei einer erneuten Zuweisung wird ein neues Objekt mit eigenem Speicherplatz erzeugt, wohingegen das Array noch auf den alten Speicherplatz verweist und daher den ursprünglichen Wert beinhaltet. Ein einfaches Überschreiben des NSNumber Wertes ist also nicht möglich, es wird immer wieder ein eigenständiges Objekt erzeugt. Auch wenn sich ein NSNumber Objekt in einem Array befindet, kann auf diesem Weg kein Wert geändert werden. Allerdings ist es möglich, ein Array Element auszutauschen. Dafür muss man aber wissen, an welcher Position im Array sich das gewünschte Objekt befindet. Den selben Variablennamen wie zuvor zu verwenden, ist hingegen problemlos möglich.
|
NSMutableArray
*myArray = [[NSMutableArray alloc] init]; NSNumber *myNumber = [NSNumber numberWithDouble:752.65]; [myArray addObject:myNumber]; NSLog(@"Number im Array: %@",[myArray objectAtIndex:0]); myNumber = [NSNumber numberWithDouble:975.92]; [myArray replaceObjectAtIndex:0 withObject:myNumber]; NSLog(@"Number im Array: %@",[myArray objectAtIndex:0]); |
Beim Vergleich zweier Werte vom Typ NSNumber ist besondere Vorsicht nötig. Eine einfache „ist gleich“ Abfrage würde nämlich abfragen, ob es das gleiche Objekt ist, also die gleiche Speicheradresse hat, nicht ob der Wert identisch ist. Da man in den meisten Fällen aber den Zahlenwert vergleichen will, muss man sich der Methode isEqualToNumber bedienen.
|
NSNumber
*number1 = [[NSNumber alloc] initWithInt:5];
NSNumber *number2 = [[NSNumber alloc] initWithInt:2]; if ([number1 isEqualToNumber:number2]) { NSLog(@"Werte sind gleich."); } else { NSLog(@"Werte sind ungleich."); } |
Als erweiterte Alternative kann auch die compare-Methode verwendet werden. Damit ist es sogar möglich zu ermitteln, welcher der zu vergleichenden Werte größer und welcher kleiner ist.
|
NSNumber
*wert1 = [NSNumber numberWithInt:5];
NSNumber *wert2 = [NSNumber numberWithInt:5]; switch ([wert1 compare:wert2]) { case NSOrderedAscending: NSLog(@"Wert 1 ist kleiner als Wert 2"); break; case NSOrderedSame: NSLog(@"Wert1 und Wert2 sind gleich."); break; case NSOrderedDescending: NSLog(@"Wert 1 ist größer als Wert 2"); break; default: NSLog(@"default"); } |
Anmerkung: Der default-Fall sollte in diesem Beispiel nicht eintreten, da alle anderen möglichen Fälle abgedeckt sind.
Auch als Container für boolsche Werten funktioniert die Klasse NSNumber nicht anders und benötig wahrscheinlich keine zusätzliche Erläuterung.
Auch als Container für boolsche Werten funktioniert die Klasse NSNumber nicht anders und benötig wahrscheinlich keine zusätzliche Erläuterung.
|
NSNumber
*myBoolNumber =[NSNumber numberWithBool:YES];
if ([myBoolNumber boolValue]) { NSLog(@"Ja"); } else { NSLog(@"Nein"); } |