NSArchiver
устарела, так как OS X 10.2, и не доступен AFAIK на прошивкойподклассов NSCoder, воссоздавая NSArchiver
С другой стороны, NSKeyedArchiver
, как известно, не хватает на краткость части скорости & (some users отчет более чем 100 раз разница рабочих характеристик между NSKeyedArchiver
и NSArchiver
). Объектами, которые я хочу архивировать, являются в основном NSObject
подклассы, содержащие NSMutableArray
из NSNumber
, и объекты, содержащие примитивные типы (в основном double
). Я не убежден в том, что накладные документы с ключами подразумевают ценность.
Итак, я решил подкласс NSCoder
на iOS создать серийный кодер в стиле NSArchiver
.
Я понимаю, где могут быть полезны архивы с ключами: совместимость в обратном направлении и другие тонкости, и, вероятно, это то, что я в конечном итоге использую, но мне было бы интересно узнать, какие выступления я могу получить с последовательным архивированием , И, честно говоря, я думаю, что я мог бы многому научиться, делая это. Так что я не заинтересован в альтернативном решении;
Я был вдохновлен источников Cocotron, обеспечивая открытый источник NSArchiver
TLDR: Я хочу подкласс NSCoder
восстановить NSArchiver
Я использую ARC, для компиляции iOS 6 & 7, и предполагая 32-битную систему на данный момент.
Я не заинтересован в ссылках объектов или строки на данный момент, я только с помощью NSHashTable
(weakObjectsHashTable
), чтобы предотвратить имена классов должны быть продублировано: классы будут описаны первый раз, когда они встречаются, а затем называют по ссылке ,
Я использую NSMutableData
для создания архива:
@interface Archiver {
NSMutableData *_data;
void *_bytes;
size_t _position;
NSHashTable *_classes;
}
@end
Основные методы:
-(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;
}
Я использую _appendBytes:length:
сваливать примитивных типов, таких как int
, char
, float
, double
... и т. д. Ничего интересного нет.
строка C-стиль сбрасывал с помощью этого одинаково неинтересного метода:
-(void)_appendCString:(const char*)cString
{
NSUInteger length = strlen(cString);
[self _appendBytes:cString length:length+1];
}
И, наконец, информация и объекты класса архивирования:
-(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];
}
В encodeWithCoder:
методах моих объектов все похожи, что, ничего необычного:
[aCoder encodeValueOfObjCType:@encode(double) at:&_someDoubleMember];
[aCoder encodeObject:_someCustomClassInstanceMember];
[aCoder encodeObject:_someMutableArrayMember];
Декодирование идет примерно так же; Unarchiver содержит NSMapTable
классов, которые он уже знает, и ищет имя ссылки на классы, которые он не знает.
@interface Unarchiver(){
NSData *_data;
const void *_bytes;
NSMapTable *_classes;
}
@end
Я не буду утомлять вас с особенностями
-(void)_extractBytesTo:(void*)data length:(NSUInteger)length
и
-(char*)_extractCString
Интересный материал, вероятно, в коде декодирования объекта:
-(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;
}
И, наконец, центральный метод (I w не ульд удивляйтесь, если проблема находится где-то здесь)
-(void)decodeValueOfObjCType:(const char *)type at:(void *)data
{
switch(*type){
/* snip, snip */
case '@':
*(id __strong *) data = [self _extractObject];
break;
}
}
Метод initWithCoder:
, соответствующее предыдущему фрагмент encodeWithCoder:
бы что-то вроде этого
if (self = [super init]) {
// Order is important
[aDecoder decodeValueOfObjCType:@encode(double) at:& _someDoubleMember];
_someCustomClassInstanceMember = [aDecoder decodeObject];
_someMutableArrayMember = [aDecoder decodeObject];
}
return self;
Мой decodeObject
реализации именно _extractObject
.
Теперь все это должно работать хорошо и хорошо. И на самом деле; Я могу архивировать/разблокировать некоторые из моих объектов. Архивы выглядят прекрасно, насколько я готов проверить их в шестнадцатеричном редакторе, и я могу разблокировать некоторые из моих пользовательских классов, содержащих NSMutableArray
s какого-либо другого класса, содержащего double
s.
Но по какой-то причине, если я пытаюсь разархивировать один мой объект, содержащий NSMutableArray
из NSNumber
, я бегу в эту проблему:
malloc: *** error for object 0xc112cc: pointer being freed was not allocated
Там, кажется, одна линия на NSNumber
в массив, а адрес 0xc112cc
- то же самое для каждой строки. Помещение точки останова в malloc_error_break
говорит мне, что ошибки от -[NSPlaceholderNumber initWithCoder:]
(вызваны из моего метода _extractObject
).
Это проблема, связанная с моим использованием ARC? Что мне не хватает?
Не могли бы вы предоставить минимальный initWithCoder: реализация? – James
Я обновил свое сообщение с помощью примера 'initWithCoder:' реализация. Это довольно просто. – Olotiar
это может иметь какое-то отношение к указателю тега, проверка '0xc112cc' фактического значения, которое вы архивируете? –