NSArchiver
自OS X 10.2起不推荐使用,并且在iOS上不可用AFAIK
在另一方面,NSKeyedArchiver
已知缺乏对速度和简明一部分(一些用户报告之间的100倍以上的性能差异NSKeyedArchiver
和NSArchiver
).我要归档的对象主要是NSObject
含子类NSMutableArray
的NSNumber
包含基本类型(主要是和对象double
).我不相信开销密钥存档意味着值得.
所以我决定NSCoder
在iOS上创建一个串行编码器的子类NSArchiver
.
我知道键控档案可能派上用场:向后兼容和其他细节,它可能是我最终会使用的,但我很想知道串行存档可以获得什么样的表现.坦率地说,我认为通过这样做我可以学到很多东西.所以我对替代解决方案不感兴趣;
我受到了Cocotron的来源的启发,提供了一个开源的NSArchiver
TLDR:我想要子类化NSCoder
来重建NSArchiver
我正在使用ARC,为iOS 6和7编译,现在假设是32位系统.
我现在对引用对象或字符串不感兴趣,我只使用a NSHashTable
(weakObjectsHashTable
)来防止类名被复制:类将在第一次遇到时被描述,然后通过引用引用.
我使用a 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; }
最后,中心方法(如果问题出在这里,我不会感到惊讶)
-(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
.
但出于某种原因,如果我尝试取消归档包含其中一个NSMutableArray
的对象NSNumber
,我会遇到这个问题:
malloc: *** error for object 0xc112cc: pointer being freed was not allocated
NSNumber
数组中似乎每行一行,并且0xc112cc
每行的地址相同.设置断点malloc_error_break
告诉我错误来自-[NSPlaceholderNumber initWithCoder:]
(从我的_extractObject
方法调用).
这是与我使用ARC有关的问题吗?我错过了什么?