另一方面,众所周知,NSKeyedArchiver缺乏速度和速度.简洁部分(some users报告NSKeyedArchiver和NSArchiver之间的性能差异超过100倍).我想归档的对象主要是包含NSNumber的NSMutableArray的NSObject子类,以及包含基本类型的对象(主要是double).我不相信开销密钥存档意味着值得.
所以我决定在iOS上继承NSCoder来创建一个NSArchiver风格的串行编码器.
我知道键控档案可能派上用场:向后兼容和其他细节,它可能是我最终会使用的,但我很想知道串行存档可以获得什么样的表现.坦率地说,我认为通过这样做我可以学到很多东西.所以我对替代解决方案不感兴趣;
我受到了Cocotron资源的启发,提供了一个开源的NSArchiver
TLDR:我想继承NSCoder来重建NSArchiver
我正在使用ARC,编译iOS 6& 7,现在假设为32bit系统.
我现在对引用对象或字符串不感兴趣,我只使用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;}
最后,中心方法(如果问题出在这里,我不会感到惊讶)
-(voID)decodeValueOfObjCType:(const char *)type at:(voID *)data{ switch(*type){ /* snip,snip */ case '@': *(ID __strong *) data = [self _extractObject]; break; }}
与之前的encodeWithCoder片段相对应的initWithCoder:方法会像这样
if (self = [super init]) { // Order is important [aDecoder decodeValueOfObjCType:@encode(double) at:& _someDoubleMember]; _someCustomClassInstanceMember = [aDecoder decodeObject]; _someMutableArrayMember = [aDecoder decodeObject]; }return self;
我的decodeObject实现完全是_extractObject.
现在,所有这些都应该很好用.事实上;我能够归档/取消归档我的一些对象.档案看起来很好,我愿意在十六进制编辑器中检查它们,并且我能够取消归档一些包含其他类包含双精度的NSMutableArrays的自定义类.
但由于某种原因,如果我尝试取消归档我的一个包含NSNumber的NSMutableArray的对象,我遇到了这个问题:
malloc: *** error for object 0xc112cc: pointer being freed was not allocated
数组中每个NSNumber似乎有一行,每行的地址0xc112cc相同.在malloc_error_break中设置断点告诉我错误来自 – [NSPlaceholderNumber initWithCoder:](从我的_extractObject方法调用).
这是与我使用ARC有关的问题吗?我错过了什么?
解决方法 我的错误与第二个参数的误解有关 – (voID)encodeValueOfObjCType:(const char *)type at:(const voID *)addr在type表示C字符串的情况下(* type ==’*’ ).在这种情况下,addr是一个const char **,一个指向const char *的指针,它本身指向应该编码的常量,0终止的char数组.NSNumber encodeWithCoder:编码一个小的C字符串,表示支持该值的变量的类型(i为int,d为double,等等,它与@encode指令的AFAIK相等).
我之前的误解(假设addr是一个const char *)进行了错误的编码/解码,并且initWithCoder:因此失败了(底线:它试图释放堆栈变量,因此错误消息和地址是每次调用函数时总是一样的).
我现在有一个有效的实施.
如果有人有兴趣,代码是在我的GitHub MIT许可证下.
以上是内存溢出为你收集整理的ios – 子类化NSCoder,重新创建NSArchiver全部内容,希望文章能够帮你解决ios – 子类化NSCoder,重新创建NSArchiver所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)