在NSUserDefaults中保存CoreData实体

19
想象一下一个 CoreData 实体(例如命名为 searchEngine)。
NSManagedObjectContext 管理这个实体的一些“实例”。
最终用户将能够使用 NSPopupButton 选择他的“标准搜索引擎”。
NSPopupButton 的 selected object 应该绑定到 NSUserDefaults。
问题是:

1) @try{save}

a) 如果您尝试直接将选定的“实例”保存到 NSUserDefaults 中,会出现以下内容:

-[NSUserDefaults setObject:forKey:]: Attempt to insert non-property value ' (entity: searchEngine; id: 0x156f60  ; data: {
    url = "http://google.de/";
    someAttribute = 1;
    name = "google";
})' of class 'searchEngine'.

b) 如果您尝试将“实例”转换为 NSData,则会出现以下内容:

-[searchEngine encodeWithCoder:]: unrecognized selector sent to instance 0x1a25b0

有什么办法可以将这些实体转换为 plist 兼容的数据吗?

2) @try{registerDefaults}

通常,registerDefaults: 方法是在 + (void)initialize 中实现的。这里的问题是,在 CoreData 从数据库加载保存的实体之前调用此方法。所以我不能为不存在的对象设置默认值,对吧?

我知道,问题很长......但是:try{[我提供:详细信息]} ;D
4个回答

35

如果您需要存储特定托管对象的引用,请使用其托管对象ID的URI表示:

NSURL *moIDURL = [[myManagedObject objectID] URIRepresentation];

你可以将URL保存到用户默认设置中。

要检索托管对象,使用:

NSManagedObjectID *moID = [myPersistentStoreCoordinator managedObjectIDForURIRepresentation:moIDURL];
NSManagedObject *myManagedObject = [myContext objectWithID:moID];

唯一的注意事项是必须确保原始托管对象ID是永久性的--如果您已经保存了对象,这不是问题,或者您可以使用 obtainPermanentIDsForObjects:error:


3
我认为这是一个不错的解决方案。还有一点需要补充,就像NSUserDefaults文档所说,您需要将NSURL对象存档为NSData。因此,小心不要直接保存NSURL。我曾犯过这个错误,尝试检索对象时一直得到空值。另请注意,NSUserDefaults有一个setURL:forKey:方法。不过我认为目前它在iPhone OS上不可用。 - tilish
7
setURL:forKey: 方法可在 iOS 4.0 及更高版本中使用。 - djskinner
1
请注意!tilish的评论很重要,正确的解决方案在这里:https://dev59.com/pXRB5IYBdhLWcg3wxZ_Y#516735。如果没有这个,你会收到错误信息:"attempt to insert non-property list object x-coredata:" - BootMaker

8

以下是目前使用4.0中添加的setURL和getURL方法最干净、最短的方式,以避免额外调用NSKeyedUnarchiver和NSKeyedArchiver:

设置器:

 + (void)storeSomeObjectId:(NSManagedObjectID *)objectId
 {
     [[NSUserDefaults standardUserDefaults] setURL:[objectId URIRepresentation] 
                                            forKey:@"someObjectIdKey"];
     [[NSUserDefaults standardUserDefaults] synchronize];
 }

获取器:

 + (SomeManagedObject *)getObjectByStoredId
 {
     NSURL *uri = [[NSUserDefaults standardUserDefaults] URLForKey:@"someObjectIdKey"];
     NSManagedObjectID *objectId = [self.persistentStoreCoordinator managedObjectIDForURIRepresentation:uri];
     SomeManagedObject *object = [self.managedObjectContext objectWithID:objectId];
 }

6

不建议尝试归档核心数据实体并存储它,相反,您可以存储关键字或其他已知属性,并在应用程序启动时使用它来获取实体。

以下是一些示例代码(略微修改自Core Data编程指南中的示例):

NSManagedObjectContext *moc = [self managedObjectContext];
NSEntityDescription *entityDescription = [NSEntityDescription
    entityForName:@"SearchEngine" inManagedObjectContext:moc];
NSFetchRequest *request = [[[NSFetchRequest alloc] init] autorelease];
[request setEntity:entityDescription];

NSPredicate *predicate = [NSPredicate predicateWithFormat:
    @"engineName LIKE[c] '%@'", selectedEngineName];
[request setPredicate:predicate];

NSError *error = nil;
NSArray *array = [moc executeFetchRequest:request error:&error];
if (array == nil)
{
    // Deal with error...
}

通过这种方式,你可以将名称保存在用户默认设置中,并在必要时提取实体。


嗯,你知道为什么 array != nil 但是 [array count] < 0 却不能正常工作吗? - papr
如果数组不等于nil,则没有错误,但也可能没有记录返回([array count] == 0)...然而,[array count] < 0永远不会发生,因为计数消息返回一个NSUInteger类型。编译器很可能会剥离任何这样的测试([array count] < 0)。 - Jason Coco
是的,我指的是([array count] == 0)。对不起,我的意思只是想表达这个数组里面没有任何东西。;) - papr

0

我的模型将记录的UUID保存到UserDefaults中,以便在下一次应用程序启动时打开最后打开的记录。

public class Patient: NSManagedObject {    
    override public func awakeFromInsert() {
        super.awakeFromInsert()
        uuid = UUID()
}

extension Patient {
    @NSManaged public var uuid: UUID
    @NSManaged ...
}

在编程中,为每个记录分配一个唯一的ID(UUID)是一个好习惯。您可以通过调用uuid.uuidString将UUID保存为字符串。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接