通过 NSSecureCoding 解码 NSArray 时的奇怪行为

Posted

技术标签:

【中文标题】通过 NSSecureCoding 解码 NSArray 时的奇怪行为【英文标题】:Strange behavoir when decoding an NSArray via NSSecureCoding 【发布时间】:2013-10-25 00:32:54 【问题描述】:

我整个下午都在用头撞墙,试图弄清楚为什么这个类的解码失败了。该类有一个属性,它是 Foo 对象的 NSArray。 Foo 符合 NSSecureCoding,我已经成功地自行编码和解码了该类。我在 initWithCoder 中遇到错误:表示解码类 Foo 失败。通过一些实验,我发现我需要将 [Foo class] 添加到 initWithCoder: 以使其工作。也许这会帮助遇到同样问题的其他人。我的问题是,为什么这是必要的?我在苹果的文档中没有发现这是必要的建议。

#import "Foo.h"

@interface MyClass : NSObject <NSSecureCoding>
@property (nonatomic) NSArray *bunchOfFoos;
@end

@implementation MyClass

static NSString *kKeyFoo = @"kKeyFoo";

+ (BOOL) supportsSecureCoding

    return YES;


- (void) encodeWithCoder:(NSCoder *)encoder

    [encoder encodeObject:self.bunchOfFoos forKey:kKeyFoo];


- (id) initWithCoder:(NSCoder *)decoder

    if (self = [super init])
    
        [Foo class]; // Without this, decoding fails
        _bunchOfFoos = [decoder decodeObjectOfClass:[NSArray class] forKey:kKeyFoo];
    
    return self;


@end

【问题讨论】:

我认为,对于您要完成的工作,您需要在 Foo 类中实现编码/解码。 是的,我说 Foo 符合 NSSecureCoding,这意味着在该类中已经实现了编码/解码。 【参考方案1】:

对于那些仍在为此苦苦挣扎的人:@Ben H 的解决方案并没有解决我的问题。而且我一直收到以下错误消息:

由于未捕获的异常“NSInvalidUnarchiveOperationException”而终止应用程序,原因:>“键“NS.objects”的值属于意外类“ClassA”。允许的类是 '( NSArray )'.'

最后,我意识到对于自定义类。您必须改用以下函数decodeObjectOfClasses:

- (id)decodeObjectOfClasses:(NSSet *)classes forKey:(NSString *)key

然后你将NSArray 中所有可能的类的NSSet 传递给上面的函数!我不知道为什么@Ben H 可以通过在函数之外简单地添加一个[Foo class] 来解决这个问题。也许这是一个编译器问题。但无论如何,如果他的解决方案不起作用,也试试这个。

【讨论】:

@Gomfucius 很高兴知道它有帮助【参考方案2】:

我刚刚遇到了类似的问题,这很奇怪而且非常耗时。我想用Specta/Expecta 测试我的班级是否正确使用 NSSecureCoded。所以我已经根据需要在解码时实现了所有指定类。在我的试验结束时,我得到了最奇怪的例外:

value for key 'key' was of unexpected class 'MyClass'. Allowed classes are '(
    MyClass
)'.

测试看起来像这样:

MyClass *myClassInstance = ...
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *secureEncoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[secureEncoder setRequiresSecureCoding:YES]; // just to ensure things

NSString *key = @"key";
[secureEncoder encodeObject:myClassInstance forKey:key];
[secureEncoder finishEncoding];

NSKeyedUnarchiver *secureDecoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
[secureDecoder setRequiresSecureCoding:YES];
MyClass *decodedInstance = [secureDecoder decodeObjectOfClass:[MyClass class] forKey:key]; // exception here
[secureDecoder finishDecoding];

...expect...

虽然普通 NSCoding (requiresSecureCoding = NO) 测试成功,但 NSSecureCoding 测试一直失败。经过大量试验后,我找到了解决方案,只需一行:

[secureDecoder setClass:[MyClass class] forClassName:NSStringFromClass([MyClass class])];

之后我的所有测试都成功了,对象按预期创建。

我不确定为什么会这样,我的猜测是该类不可见,正如Ben H 建议的那样,它使用类似NSClassFromString(@"MyClass") 的东西。上面的代码在 AppDelegate 中运行良好。 MyClass 来自我正在开发的开发 pod。

【讨论】:

【参考方案3】:

我想我可能已经想通了。如果没有 [Foo class] 行,则此文件中没有对 Foo 类的引用。因此,我相信编译器正在优化 Foo 类,然后无法解码数组中的 Foo 对象。有 [Foo class] 可以防止这种情况发生。

【讨论】:

【参考方案4】:

Yuchen 的回答是/是在正确的轨道上,但重要的是要知道,除了自定义类之外,NSSet 参数还需要包含集合的类,如下所示:

_bunchOfFoos = [decoder decodeObjectOfClasses:[NSSet setWithObjects:[NSArray class], [Foo class], nil] forKey:kKeyFoo];

至少目前看来这对我有用...

【讨论】:

以上是关于通过 NSSecureCoding 解码 NSArray 时的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 3 中使用 NSSecureCoding 解码字符串?

NSSecureCoding 与钥匙串

NSSecureCoding 实现类必须在共享框架中才能与 XPC 一起使用吗?

通过图像更改为单个 UIImageView 编写多个动画

iOS 仿照今日头条 实现的滚动表格 XLSlideSwitch

技术开发:H.265编码视频在web网页实现无插件播放应该通过软解码还是硬解码?