用Core Data存储自定义对象

我试图存储一个NSMutableArray组成的VOs(NameVO)与核心数据,但得到下面的exception抛出:

'NSInvalidArgumentException', reason: '-[NameVO encodeWithCoder:]: unrecognized selector sent to instance 0x1096400a0' 

我的NameVO只是一个简单的值对象,扩展NSObject并包含两个字段,一个string和一个NSMutableArray本身包含string。 它还包含一个比较function。

我试图准备这个存储为一个CD可转换的属性types('名称'是我的NameVO数组):

 NSData *tempNamesData = [NSKeyedArchiver archivedDataWithRootObject:names]; 

我的问题是:我需要做什么使NameVO被NSKeyedArchiver接受成功转换为NSData?

我不希望NameVO扩展NSManagedObject,因为那样我就不能直接实例化和初始化它。

作为你的例外说你需要实现NSCoding协议到你的类,你必须重写两种方法:

 - (void)encodeWithCoder:(NSCoder *)aCoder; - (id)initWithCoder:(NSCoder *)aDecoder; 

它应该sorting你的问题。

//扩展

 - (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { _formId = [aDecoder decodeObjectForKey:@"FormID"]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.formId forKey:@"FormID"]; } 

使用这个initWithCoderencodeWithCode方法。我希望它能为你工作。 它适用于我在同样的问题,你有…使用此示例代码

 -(id)initWithCoder:(NSCoder *)aDecoder { if(self = [super init]){ storePlaylist=[aDecoder decodeObjectForKey:@"storePlaylist"]; playlistName=[aDecoder decodeObjectForKey:@"playlistName"]; } return self; } -(void)encodeWithCoder:(NSCoder *)encoder { [encoder encodeObject:storePlaylist forKey:@"storePlaylist"]; [encoder encodeObject:playlistName forKey:@"playlistName"]; } 

现在我们已经到了2017年了,最好使用更安全的NSSecureCoding协议,而不要使用旧的,不太安全的NSCoding协议。 实施变化很小:

1)确保你的类声明它的符合NSSecureCoding

 @interface MyClass : NSObject<NSSecureCoding> @property (nonatomic, strong) NSNumber *numberProperty; @property (nonatomic, strong) NSString *stringProperty; @end 

2)NSSecureCoding协议在NSCoding协议中包含相同的两个实例方法方法,另外还有一个额外的类方法+supportsSecureCoding 。 您需要添加该方法,以及稍微修改您的-initWithCoder:方法。

 @implementation MyClass // New Method for NSSecureCoding. Return YES. + (BOOL)supportsSecureCoding { return YES; } // Your Encode Method Can Stay The Same, though I would use NSStringFromSelector whenever possible to get the keys to ensure you're always getting what you're looking for. - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.numberProperty forKey:NSStringFromSelector(@selector(numberProperty))]; [aCoder encodeObject:self.stringProperty forKey:NSStringFromSelector(@selector(stringProperty))]; } // Slightly updated decode option - (instancetype)initWithCoder:(NSCoder *)aDecoder { self = [super init]; if (self) { self.numberProperty = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(numberProperty))]; self.stringProperty = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(stringProperty))]; } } @end 

注意,NSCoder的-decodeObjectOfClass:withKey:要求你指定你期望接收的类。 这是一个更安全的方式来做事情。

然后,要将这个可解码对象存储在CoreData中,只需创build一个包含NSData属性和一些标识信息(string,date,ID或数字或其他)的托pipe对象,

 @interface MyClassMO : NSManagedObject @property (nonatomic, strong) NSString *identifier; @property (nonatomic, strong) NSData *data; @end @implementation MyClassMO @dynamic identifier; @dynamic data; @end 

在实践中,它看起来像这样:

 - (void)storeObject:(MyClass *)object withIdentifier:(NSString *)identifier { NSData *objectData = [NSKeyedArchived archivedDataWithRootObject:object]; NSManagedObjectContext *moc = ... // retrieve your context // this implementation relies on new NSManagedObject initializers in the iOS 10 SDK, but you can create it any way you typically create managed objects MyClassMO *managedObject = [[MyClassMO alloc] initWithContext:moc]; managedObject.data = objectData; managedObject.identifier = identifier; NSError *error; [moc save:&error]; } - (MyClass *)retrieveObjectWithIdentifier(NSString *)identifier { NSManagedObject *moc = ... // retrieve your context // This also relies on iOS 10 SDK's new factory methods available on NSManagedObject. You can create your request any way you typically do; NSFetchRequest *request = [MyClassMO fetchRequest]; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"identifier = %@", identifier]; request.predicate = predicate; NSError *error; NSArray<MyClassMO *> *results = [moc executeFetchRequest:request withError:&error]; // if you're only storing one object per identifier, this array should only be 1 object long. if not, you'll need to decide which object you're looking for. you also might want to implement an overwrite policy or a delete before store type thing. MyClassMO *managedObject = results.firstObject; NSData *objectData = managedObject.data; MyClass *object = [NSKeyedUnarchiver unarchiveObject:objectData]; return object; } 

这个解决scheme显然有些过分简单化,如何以及何时将数据存储在数据库中取决于您的需求,但主要的想法是,您需要确保您的自定义类符合NSSecureCoding,您需要制作一个单独的Managed Object类来存储和检索您的数据。