2010-09-15 11 views
25

Wie kann ich eine NSUInteger mit dem NSCoding Protokoll speichern, vorausgesetzt, dass es keine Methode auf NSCoder wie -encodeUnsignedInteger:(NSUInteger)anInt forKey:(NSString *)aKey gibt, wie es für NSInteger gibt?Wie speichert man einen NSUInteger mit NSCoding?

Folgendes funktioniert, aber ist dies der beste Weg, dies zu tun? Dies erzeugt unnötigerweise Objekte.

@interface MYObject : NSObject <NSCoding> { 
    NSUInteger count; 
} 

- (void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:@"count"]; 
} 

- (id)initWithCoder:(NSCoder *)decoder { 
    self = [super init]; 
    if (self != nil) { 
     count = [[decoder decodeObjectForKey:@"count"] unsignedIntegerValue]; 
    } 
    return self; 
} 
+1

Ich würde Ihren Code nicht für das Erstellen von Objekten nicht optimieren, wenn Sie es zuerst schreiben. Es ist besser, etwas zum Laufen zu bringen, als den Code, den Sie schreiben müssen, zu analysieren. Du wirst zu viel Zeit damit verschwenden zu denken und nicht genug Zeit zu haben. Sie möchten die Leistung später messen und Änderungen vornehmen, wenn die Objekterstellung zu einem Problem wird. Profiling in Xcode ist ein großartiges Tool zum Erlernen. –

Antwort

11

Das Problem ist, dass NSUInteger möglicherweise nicht die gleiche Größe wie eine unsigned int haben. Auf vielen (wenn nicht sogar allen) Mac OS X Maschinen wird NSUInteger eine unsigned long sein, die doppelt so groß sein wird. Auf der anderen Seite sollte ein verschlüsselter Archiver das problemlos handhaben, da er ein Wörterbuch erstellt.

Noch komplizierter ist die Tatsache, dass NSCoder keine Methoden hat, um mit unsignierten Typen umzugehen. Ich kann mir nicht vorstellen, wie dies zu Datenverlust führen würde, aber es würde einige hässliche Würfe erfordern und es fühlt sich einfach nur schmutzig an.

Wenn Sie auf einen Typ ohne Vorzeichen bestehen wollen, wäre der einfachste Weg, die Roh-Bytes zu kodieren (vorzugsweise in Netzwerk-Byte-Reihenfolge htonl und ntohl verwendet) in der größten Art zur Verfügung (unsigned long long) mit encodeBytes:length:forKey: und decodeBytesForKey:returnedLength:. Um maximale Sicherheit zu gewährleisten, sollten Sie die Länge der dekodierten Objekte überprüfen und den Zeiger (oder eine Union) umwandeln, um den Typ mit der korrekten Größe zu extrahieren.

Der Nachteil davon ist, dass der Wert in der Ausgabe als Daten, keine ganze Zahl dargestellt wird. Das ist hauptsächlich wichtig, nur wenn jemand entscheidet, die rohen plist-Daten für Ihr Archiv einzulesen, anstatt den verschlüsselten Unarchivierer wie Sie und nur dann nur für sie zu verwenden. Die anderen Fälle, in denen es darauf ankommt, ist, ob Apple (oder Sie) jemals zu einer Architektur mit noch größeren Integer-Typen wechseln sollte, deren Größe in Bits keine Zweierpotenz ist (es gibt mindestens eine alte Plattform, auf der ein Wort 24 war) Bits) oder Typen mit einem ungewöhnlichen Layout (nicht Big- oder Little-Endian).

Wie für Ihre NSNumber-Lösung: Sie möchten möglicherweise öffnen Sie Ihr Archiv im Eigenschaftenlisten-Editor und sehen, was es emittiert. Wenn die Ausgabe ein Integer-Element enthält, entspricht dies der Verwendung von encodeInteger:forKey:. Wenn die Ausgabe ein Datenelement enthält, ist es dasselbe wie die oben erwähnte Lösung. Um gründlich zu sein, sollten Sie die Ausgabe von jeder unterstützten Architektur überprüfen.

+0

Ich denke nicht * es ist notwendigerweise wahr, dass, wenn die Ausgabe eine Ganzzahl enthält, es dasselbe wie 'encodeInteger: forKey:' ist. Plist-Integer sind Strings mit unendlicher Länge, während 'encodeInteger: forKey:' das Argument notwendigerweise in den vorzeichenbehafteten Integer-Bereich konvertiert. – Chuck

+0

@Chuck: Nur überprüft. Die NSNumber-Lösung behandelt Zahlen über 'NSIntegerMax' korrekt. Ein Fehler jedoch: Im 32-Bit-Modus wird das Einlesen einer Zahl, die größer als die 64-Bit-Zahl "NSIntegerMax" ist, als 0 zurückkommen. @Johan Kool: Sie könnten 'encodeInt64: forKey:'/'decodeInt64ForKey verwenden: 'stattdessen. –

-1

Sie gehen NSCoder des

encodeInt:ForKey: 

und

decodeIntForKey: 

Methoden verwenden wollen. Also, in Ihrem Fall würden Sie brauchen:

- (void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeInt:count forKey:@"count"]; 
} 

- (id)initWithCoder:(NSCoder *)decoder { 
    self = [super init]; 
    if (self != nil) { 
     count = [decoder decodeIntForKey:@"count"]; 
    } 
    return self; 
} 

Hoffnung, die hilft, auch, es gibt viel mehr kodieren „kodieren“ Methoden, werfen Sie einen Blick in die Dokumentation für NSCoder: D

+0

Ich kenne diese Methode (und mit der Dokumentation). Meine Frage ist, ob das sicher wäre, da "int" und "NSUInteger" nicht dasselbe sind. –

+0

@Peter Das ist, was ich dachte. Kann ich einen 'NSUInteger' codieren, ohne ihn in' NSNumber' zu schreiben oder Informationen zu verlieren? –

+0

Ja. Technisch ist NSUInteger einfach ein Alias ​​für eine vorzeichenlose Ganzzahl, dafür steht das "U". Auch hier ist es sicher, encodeInt zu verwenden: mit einem NSUInteger. –

2

Das ist der beste Weg ist, es zu tun. Es scheint wie unnötige Arbeit, aber es gibt keinen anderen verfügbaren Typ, der die gesamte Palette von Werten, die NSUInteger halten kann, portabel darstellen kann.

+0

Diese Antwort steht in direktem Widerspruch zu dem, was Matt Egan in seiner Antwort/Kommentar sagte. Ich denke, du hast Recht. –

26

NSNumber verfügt über viele Methoden zum Speichern/Abrufen unterschiedlich großer und signierter Typen. Es ist die einfachste Lösung und erfordert kein Byte-Management wie andere Antworten vorschlagen.Hier

sind die Typen nach Apple-Dokumentation auf NSNumber:

+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value 
- (NSUInteger)unsignedIntegerValue 

Ja Ihr Codebeispiel der beste Weg ist, um die NSUInteger zu kodieren/dekodieren. Ich würde empfehlen, Konstanten für die Schlüsselwerte zu verwenden, damit Sie sie nicht falsch eingeben und Archivierungsfehler einführen.

static NSString * const kCountKey = @"CountKey"; 

@interface MyObject : NSObject <NSCoding> { 
    NSUInteger count; 
} 

@end 

@implementation MyObject 

- (void)encodeWithCoder:(NSCoder *)encoder { 
    [encoder encodeObject:[NSNumber numberWithUnsignedInteger:count] forKey:kCountKey]; 
} 

- (id)initWithCoder:(NSCoder *)decoder { 
    self = [super init]; 
    if (self != nil) { 
     count = [[decoder decodeObjectForKey:kCountKey] unsignedIntegerValue]; 
    } 
    return self; 
} 
@end