NSArchiver
seit O X ist veraltet 10.2 und ist AFAIK nicht auf iOSSubclassing NSCoder, Neue NSArchiver
Auf der anderen Seite wird NSKeyedArchiver
bekannt von der Geschwindigkeit & Prägnanz Teil zu fehlen (some users Bericht mehr als 100 mal Leistungsunterschied zwischen NSKeyedArchiver
und NSArchiver
). Die Objekte, die ich archivieren möchte, sind hauptsächlich NSObject
Unterklassen, die NSMutableArray
von NSNumber
enthalten, und Objekte, die primitive Typen enthalten (hauptsächlich double
). Ich bin nicht davon überzeugt, dass die Overhead-Keyed-Archivierung es wert ist.
Also habe ich beschlossen, NSCoder
auf iOS unterteilen, um einen seriellen Coder im Stil von NSArchiver
zu erstellen.
Ich verstehe, wo verschlüsselte Archive nützlich sein könnten: rückwärts Vorwärtskompatibilität und andere Feinheiten, und es ist wahrscheinlich, was ich am Ende verwenden werde, aber ich wäre gespannt, welche Art von Leistungen ich mit serieller Archivierung bekommen könnte . Und ehrlich gesagt denke ich, dass ich dadurch viele Dinge lernen könnte. So bin ich nicht interessiert an einer alternativen Lösung;
Ich habe von den Quellen von Cocotron inspiriert, eine Open-Source-Bereitstellung NSArchiver
TLDR: Ich möchte NSCoder
Unterklasse NSArchiver
Ich bin mit ARC neu zu erstellen, für die Erstellung iOS 6 & 7 und 32bit-System für jetzt vorausgesetzt.
Ich bin in Referenzierung Objekte oder Zeichenfolgen für jetzt nicht interessiert, ich bin nur mit einem NSHashTable
(weakObjectsHashTable
) Klassen-Namen zu verhindern dupliziert werden: Klassen wird das erste Mal beschrieben sie angetroffen werden, und verweist dann auf die durch Bezugnahme .
ich ein NSMutableData
bin mit dem Archiv zu erstellen:
@interface Archiver {
NSMutableData *_data;
void *_bytes;
size_t _position;
NSHashTable *_classes;
}
@end
Die grundlegenden Methoden sind:
-(void)_expandBuffer:(NSUInteger)length
{
[_data increaseLengthBy:length];
_bytes = _data.mutableBytes;
}
-(void)_appendBytes:(const void *)data length:(NSUInteger)length
{
[self _expandBuffer:length];
memcpy(_bytes+_position, data, length);
_position += length;
}
ich _appendBytes:length:
bin mit primitiven Typen dump wie int
, char
, float
, double
... etc. Nichts Interessantes dort.
C-Strings mit dieser ebenso uninteressant Methode des Einbringens:
-(void)_appendCString:(const char*)cString
{
NSUInteger length = strlen(cString);
[self _appendBytes:cString length:length+1];
}
Und schließlich Archivierungsklasse Informationen und Objekten:
-(void)_appendReference:(id)reference {
[self _appendBytes:&reference length:4];
}
-(void)_appendClass:(Class)class
{
// NSObject class is always represented by nil by convention
if (class == [NSObject class]) {
[self _appendReference:nil];
return;
}
// Append reference to class
[self _appendReference:class];
// And append class name if this is the first time it is encountered
if (![_classes containsObject:class])
{
[_classes addObject:class];
[self _appendCString:[NSStringFromClass(class) cStringUsingEncoding:NSASCIIStringEncoding]];
}
}
-(void)_appendObject:(const id)object
{
// References are saved
// Although we don't handle relationships between objects *yet* (we could do it the exact same way we do for classes)
// at least it is useful to determine whether object was nil or not
[self _appendReference:object];
if (object==nil)
return;
[self _appendClass:[object classForCoder]];
[object encodeWithCoder:self];
}
Die encodeWithCoder:
Methoden meiner Objekte all das aussehen, nichts Besonderes:
[aCoder encodeValueOfObjCType:@encode(double) at:&_someDoubleMember];
[aCoder encodeObject:_someCustomClassInstanceMember];
[aCoder encodeObject:_someMutableArrayMember];
Decoding geht ziemlich genau so; Der Unarchivierer enthält eine NSMapTable
Klasse, die er bereits kennt und sucht nach dem Namen der Klassenreferenz, die er nicht kennt.
@interface Unarchiver(){
NSData *_data;
const void *_bytes;
NSMapTable *_classes;
}
@end
Ich will Sie nicht langweilen mit den Besonderheiten von
-(void)_extractBytesTo:(void*)data length:(NSUInteger)length
und
-(char*)_extractCString
Das interessante Sachen ist wahrscheinlich in dem Objekt Decodierungscode:
-(id)_extractReference
{
id reference;
[self _extractBytesTo:&reference length:4];
return reference;
}
-(Class)_extractClass
{
// Lookup class reference
id classReference = [self _extractReference];
// NSObject is always nil
if (classReference==nil)
return [NSObject class];
// Do we already know that one ?
if (![_classes objectForKey:classReference])
{
// If not, then the name should follow
char *classCName = [self _extractCString];
NSString *className = [NSString stringWithCString:classCName encoding:NSASCIIStringEncoding];
free(classCName);
Class class = NSClassFromString(className);
[_classes setObject:class forKey:classReference];
}
return [_classes objectForKey:classReference];
}
-(id)_extractObject
{
id objectReference = [self _extractReference];
if (!objectReference)
{
return nil;
}
Class objectClass = [self _extractClass];
id object = [[objectClass alloc] initWithCoder:self];
return object;
}
Und schließlich, die zentrale Methode (I w etwas wie die
if (self = [super init]) {
// Order is important
[aDecoder decodeValueOfObjCType:@encode(double) at:& _someDoubleMember];
_someCustomClassInstanceMember = [aDecoder decodeObject];
_someMutableArrayMember = [aDecoder decodeObject];
}
return self;
Meiner decodeObject
Implementierung gehen würde, ist genau _extractObject
Ould nicht überrascht, wenn das Problem irgendwo hier)
-(void)decodeValueOfObjCType:(const char *)type at:(void *)data
{
switch(*type){
/* snip, snip */
case '@':
*(id __strong *) data = [self _extractObject];
break;
}
}
Die initWithCoder:
Methode zum vorherigen Snippet encodeWithCoder:
entspricht.
Jetzt sollte das alles gut und schön funktionieren. Und tatsächlich; Ich kann einige meiner Objekte archivieren/entpacken. Die Archive sehen gut aus, soweit ich bereit bin, sie in einem Hex-Editor zu untersuchen, und ich kann einige meiner benutzerdefinierten Klassen, die NSMutableArray
s einer anderen Klasse enthalten, die double
enthält.
Aber aus irgendeinem Grund, wenn ich versuche, eine meiner Objekt aus dem Archiv entfernen eine NSMutableArray
von NSNumber
, enthält betreibe ich in dieses Problem:
malloc: *** error for object 0xc112cc: pointer being freed was not allocated
Es scheint eine Zeile zu sein pro NSNumber
in der Array, und die Adresse 0xc112cc
ist für jede Zeile gleich. Setzen Sie einen Breakpoint in malloc_error_break
sagt mir, dass der Fehler ist von -[NSPlaceholderNumber initWithCoder:]
(von meinem _extractObject
Methode aufgerufen).
Ist das ein Problem mit meiner Verwendung von ARC verbunden? Was vermisse ich ?
Können Sie eine minimale initWithCoder: Implementierung teilen? – James
Ich habe meinen Beitrag mit einer Beispiel-Implementierung von 'initWithCoder:' aktualisiert. Es ist ziemlich einfach. – Olotiar
kann es etwas mit dem Tag-Zeiger zu tun haben, check ist '0xc112cc' der tatsächliche Wert, den Sie archivieren? –