Reputation: 800
I already take a look around and found the closes answer here:
But this is only applicable for custom objects that is user-created. My problem is serializing "SKProduct" which is a derived class that is non-NSCoding compliant. Specifically, the exact error I encountered:
-[SKProduct encodeWithCoder:]: unrecognized selector sent to instance 0x4027160
Does anyone have similar experience?
Upvotes: 2
Views: 1471
Reputation: 9185
I'll preface this answer by saying there's probably an easier way; but class substitution during archiving and unarchiving is one approach you could take.
During archiving, you have the option of setting a delegate that conforms to the NSKeyedArchiverDelegate
protocol. All of the methods are optional. The delegate receives a message archiver:willEncodeObject:
message during encoding. If you wish to substitute a class during archiving, you can create a substitute object and return it. Otherwise just return the original object.
In your case, you could create a 'shadow object' for SKProduct
that encapsulates whatever properties on the original class you are interested in serializing. Then substitute that class during archiving. During unarchiving, you could reverse the process and return SKProduct
For illustrative purposes, here's an example. Mind you, I've left out the reverse substitution part - but if you read the docs on NSKeyedUnarchiverDelegate
I think it would be clear.
#import <Foundation/Foundation.h>
@interface NoncompliantClass:NSObject
@property (nonatomic,assign) NSInteger foo;
@end
@implementation NoncompliantClass
@synthesize foo = _foo;
@end
@interface CompliantClass:NSObject <NSCoding>
@property (nonatomic,assign) NSInteger foo;
@end
@implementation CompliantClass
@synthesize foo = _foo;
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeInteger:self.foo forKey:@"FooKey"];
}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
if( !self ) { return nil; }
_foo = [coder decodeIntegerForKey:@"FooKey"];
return self;
}
@end
@interface ArchiverDelegate:NSObject <NSKeyedArchiverDelegate>
@end
@implementation ArchiverDelegate
- (id)archiver:(NSKeyedArchiver *)archiver willEncodeObject:(id)object {
NSString *objClassName = NSStringFromClass([object class]);
NSLog(@"Encoding %@",objClassName);
if( [object isMemberOfClass:[NoncompliantClass class]] ) {
NSLog(@"Substituting");
CompliantClass *replacementObj = [CompliantClass new];
replacementObj.foo = [object foo];
return replacementObj;
}
return object;
}
@end
int main(int argc, char *argv[]) {
@autoreleasepool {
NoncompliantClass *cat1 = [NoncompliantClass new];
NoncompliantClass *cat2 = [NoncompliantClass new];
NSArray *herdableCats = [NSArray arrayWithObjects:cat1,cat2,nil];
ArchiverDelegate *delegate = [ArchiverDelegate new];
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver setDelegate:delegate];
[archiver encodeObject:herdableCats forKey:@"badKitties"];
[archiver finishEncoding];
}
}
This logs:
2012-09-18 05:24:02.091 TestSerialization[10808:303] Encoding __NSArrayI
2012-09-18 05:24:02.093 TestSerialization[10808:303] Encoding NoncompliantClass
2012-09-18 05:24:02.093 TestSerialization[10808:303] Substituting
2012-09-18 05:24:02.094 TestSerialization[10808:303] Encoding NoncompliantClass
2012-09-18 05:24:02.094 TestSerialization[10808:303] Substituting
Upvotes: 2