2009-07-19 15 views
0

Ich arbeite mit Objective-C und ich muss hinzufügen Int von einem NSArray zu einem NSMutableData (Ich bereite ein, um die Daten über eine Verbindung zu senden). Wenn ich die int mit NSNumber umschließe und sie dann NSMutableData hinzufüge, wie würde ich herausfinden, wie viele Bytes in der NSNumber int sind? Wäre es möglich, sizeof() zu verwenden, da laut der Apple-Dokumentation "NSNumber eine Unterklasse von NSValue ist, die einen Wert wie jeden C-skalaren (numerischen) Typ anbietet."?Fügt NSNumber der Zahl, die es enthält, zusätzliche Bytes hinzu?

Beispiel:

NSNumber *numero = [[NSNumber alloc] initWithInt:5]; 

NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0]; 

[data appendBytes:numero length:sizeof(numero)]; 

Antwort

6

numero ist kein numerischer Wert ist, ist es ein Zeiger auf ein Objekt einen numerischen Wert represting. Was Sie versuchen zu tun, wird nicht funktionieren, die Größe wird immer gleich einem Zeiger sein (4 für 32-Bit-Plattformen und 8 für 64-Bit), und Sie werden einen Müll-Zeiger-Wert an Ihre Daten im Gegensatz zu der Zahl anhängen.

Selbst wenn Sie versuchen sollten, es zu dereferenzieren, können Sie nicht direkt auf die Bytes zugreifen, die eine NSNumber unterstützen, und erwarten, dass es funktioniert. Was läuft, ist ein internes Implementierungsdetail und kann von Release zu Release oder sogar zwischen verschiedenen Konfigurationen desselben Releases variieren (32 Bit gegenüber 64 Bit, iPhone vs Mac OS X, Arm vs i386 vs PPC). Das Packen der Bytes und das Senden über die Leitung kann zu etwas führen, das auf der anderen Seite nicht richtig deserialisiert wird, selbst wenn es Ihnen gelungen ist, zu den eigentlichen Daten zu gelangen.

Sie müssen wirklich eine Codierung einer Ganzzahl, die Sie in Ihre Daten einfügen können, und dann packen und entpacken Sie die NSNumbers in das. Etwas wie:

NSNumber *myNumber = ... //(get a value somehow) 
int32_t myInteger = [myNumber integerValue]; //Get the integerValue out of the number 
int32_t networkInteger = htonl(myInteger); //Convert the integer to network endian 
[data appendBytes:&networkInteger sizeof(networkInteger)]; //stuff it into the data 

Auf der Empfangsseite Du dann die ganze Zahl greifen und eine NSNumber mit numberWithInteger neu: nach ntohl Verwendung zu nativen Host-Format zu konvertieren.

Es ist ein bisschen mehr Arbeit erfordern, wenn Sie minimale Darstellungen zu senden versuchen, usw.

Die andere Option ist eine NSCoder Unterklasse zu verwenden und den NSNumber sagen zu kodieren selbst Ihre Codierer verwenden, da dies sein Plattform neutral, aber es kann für das, was Sie versuchen zu tun, übertrieben sein.

+0

Ich bin immer noch den Fehler bekommen: "Warnung: Übergabe des Arguments 1 von 'appendBytes: Länge:' macht Zeiger von Ganzzahl ohne cast" –

+0

Das sollte letzte Zeile sein: [data appendBytes: & networkInteger sizeof (networkInteger)] ; Beachten Sie das "&". –

+0

Das hat es getan ... danke. Nebenbei bemerkt wollte ich nur darauf hinweisen, dass in der Dokumentation von Apple steht, dass "length:" vor der sizeof() steht. Es ist Teil des Methodennamens. Das vollständige Beispiel in ihrer Dokumentation sieht ungefähr so ​​aus; - (void) appendBytes: (const void *) Bytes Länge: (NSUInteger) Länge Ich wollte nichts sagen, aber wenn zwei Menschen dies nicht sehen, ich dachte, dass ich es erwähnen sollte. –

2

Zuerst ist NSNumber * numero "Ein Zeiger auf einen NSNumber-Typ" und der NSNumber-Typ ist ein Objective-C-Objekt. Wenn in der Dokumentation nicht anders angegeben, gilt im Allgemeinen die Faustregel der objektorientierten Programmierung: "Die internen Details, wie ein Objekt seinen internen Zustand darstellt, sind für die Implementierung der Objekte privat und sollten als Schwarz behandelt werden Box." Auch wenn in der Dokumentation nicht anders angegeben, können Sie nicht davon ausgehen, dass NSNumber einen primitiven C-Typ int verwendet, um den von Ihnen angegebenen Wert int zu speichern.

Das Folgende ist eine grobe Annäherung an, was los ist ‚hinter den Kulissen‘, wenn Sie appendBytes:numero:

typedef struct { 
    Class isa; 
    double dbl; 
    long long ll; 
} NSNumber; 

NSNumber *numero = malloc(sizeof(NSNumber)); 
memset(numero, 0, sizeof(NSNumber)); 
numero->isa = objc_getClass("NSNumber"); 

void *bytes = malloc(1024); 

memcpy(bytes, numero, sizeof(numero)); // sizeof(numero) == sizeof(void *) 

Dies macht es ein bisschen mehr klar, dass data zum NSMutableData Objekt, was Sie Anfügen das ist die ersten vier Bytes von was immer numero zeigt auf (was für ein Objekt in Obj-C immer isa ist, die Objektklasse). Ich vermute, was Sie wollten, war, den Zeiger auf das instanziierte Objekt (den Wert von Numero) zu kopieren, in diesem Fall hätten Sie &numero verwenden sollen. Dies ist ein Problem, wenn Sie GC verwenden, da der Puffer, der von NSMutableData verwendet wird, nicht gescannt wird (dh das GC-System wird das Objekt nicht mehr "sehen" und es zurückgewinnen, was eine Garantie für einen zufälligen Absturz ist Punkt.)

Es ist hoffentlich klar, dass selbst wenn Sie den Zeiger auf das instanziiert NSNumber Objekt in zu data setzen, dass Zeiger nur im Rahmen des Prozesses Bedeutung hat, die es erstellt. Ein Zeiger auf dieses Objekt ist noch weniger aussagekräftig, wenn Sie diesen Zeiger an einen anderen Computer senden - der empfangende Computer hat keine (praktische, triviale) Möglichkeit, den Speicher zu lesen, auf den der Zeiger im sendenden Computer zeigt.

Da Sie scheinen Probleme mit diesem Teil des Prozesses zu haben, lassen Sie mich eine Empfehlung aussprechen, dass Sie unzählige Stunden Debuggen einige extrem schwierige Implementierung Fehler Sie sind verpflichtet, sparen, um zu laufen:

Abandon Diese ganze Idee versucht, rohe binäre Daten zwischen Maschinen zu senden und nur einfache ASCII/UTF-8-formatierte Informationen zwischen ihnen zu senden.

Wenn Sie denken, dass dies etwas langsam oder ineffizient sein wird, dann lassen Sie mich empfehlen, dass Sie alles mit einer vereinfachten ASCII/UTF-8-String-Version zuerst heraufbringen. Vertrauen Sie mir, das Debuggen roher Binärdaten ist kein Spaß, und die Fähigkeit, nur NSLog(@"I got: %@", dataString) ist Gold wert, wenn Sie Ihre unvermeidlichen Probleme zu debuggen. Wenn dann alles geliert ist und Sie sicher sind, dass Sie keine weiteren Änderungen an dem vornehmen müssen, was Sie austauschen müssen, "port" (mangels eines besseren Wortes) diese Implementierung in eine binäre Version if, und nur wenn, Profilierung mit Shark.app identifiziert es als ein Problembereich. Als Bezugspunkt kann ich heute eine Datei zwischen Rechnern scp und eine Gigabit-Verbindung mit der Übertragung sättigen. scp hat wahrscheinlich etwa fünftausend mal so viel Verarbeitung pro Byte zu komprimieren und verschlüsseln die Daten als diese einfache Stringification alle während der Übertragung von 80MB/sec. Auf moderner Hardware reicht dies jedoch kaum aus, um den CPU-Zähler in meiner Menüleiste zu bewegen.

Verwandte Themen