ios – 子类化NSCoder,重新创建NSArchiver

ios – 子类化NSCoder,重新创建NSArchiver,第1张

概述自OS X 10.2起,NSArchiver已弃用,并且在iOS上不可用AFAIK 另一方面,众所周知,NSKeyedArchiver缺乏速度和速度.简洁部分(some users报告NSKeyedArchiver和NSArchiver之间的性能差异超过100倍).我想归档的对象主要是包含NSNumber的NSMutableArray的NSObject子类,以及包含基本类型的对象(主要是doubl 自OS X 10.2起,NSArchiver已弃用,并且在iOS上不可用AFAIK

另一方面,众所周知,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所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/web/1016376.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-22
下一篇 2022-05-22

发表评论

登录后才能评论

评论列表(0条)

保存