问题
i spent all afternoon banging my head against the wall trying to figure out why decoding of this class was failing. the class has a property that is an NSArray of Foo objects. Foo conforms to NSSecureCoding, and i have successfully encoded and decoded that class by itself. i was getting an error in initWithCoder: that said failed to decode class Foo. through some experimentation, i discovered that i needed to add [Foo class] to initWithCoder: in order for it to work. maybe this will help someone else who's having the same problem. my question is, why is this necessary? i found no suggestion that this is necessary in apple's documentation.
#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
回答1:
For those who are still struggling with this: @Ben H's solution didn't solve my problem. And I keep having the following error message:
Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: >'value for key 'NS.objects' was of unexpected class 'ClassA'. Allowed classes are '{(
NSArray
)}'.'
And finally, I realized that for custom classes. You have to use the following function instead decodeObjectOfClasses:
- (id)decodeObjectOfClasses:(NSSet *)classes forKey:(NSString *)key
And you to pass a NSSet
of all possible classes in the NSArray
to the function above! I am not sure why @Ben H could solve the issue by simply adding a [Foo class]
outside of the function. Maybe it is a compiler issue. But anyway, if his solution doesn't work, try this one as well.
回答2:
I've just encountered similar issue and that was weird and extremely time consuming. I wanted to test my class to be NSSecureCoded correctly with Specta/Expecta. So I've implemented everything as needed specifying class when decoded. At the end of my trials I got weirdest exception:
value for key 'key' was of unexpected class 'MyClass'. Allowed classes are '{(
MyClass
)}'.
Test looked something like that:
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...
While plain NSCoding (requiresSecureCoding = NO) test succeeded, NSSecureCoding tests kept failing. After vast range of trials I found solution for that, just a single line:
[secureDecoder setClass:[MyClass class] forClassName:NSStringFromClass([MyClass class])];
After that all my tests succeeded, objects were created as expected.
I'm not sure why did that happened, my guess would be that class is not visible as Ben H
suggested and it uses something like NSClassFromString(@"MyClass")
. The above code worked fine in AppDelegate. MyClass was from development pods I'm developing.
回答3:
i think i may have figured this out. without the line [Foo class], there is no reference to the class Foo in this file. because of this, i believe the compiler is optimizing the Foo class out, and then the Foo objects within the array cannot be decoded. having [Foo class] in there prevents this.
来源:https://stackoverflow.com/questions/19579143/strange-behavoir-when-decoding-an-nsarray-via-nssecurecoding