我在处理CoreData中的对象ID时遇到问题。我使用MagicalRecord是为了方便,并且有3个上下文:私有队列工作上下文,UI的主队列上下文和工作上下文的父级,以及作为主上下文的父级的私有队列保存上下文。
我的目标是在工作上下文中创建一个对象,保存到持久存储,将它的objectID URL保存到NSUserDefaults,然后能够使用objectID拉出该MO。但是,我发现保存对象的永久ID后正在改变。
在下面的控制台输出中,您可以看到在我请求永久ID后,我得到的值是“F474F6EE-A225-456B-92EF-AB1407336F15 / CDBaseAccount / p1”但稍后当我列出CD中的所有对象时只有对象有ID“F474F6EE-A225-456B-92EF-AB1407336F15 / CDBaseAccount / p2”。 p1 vs p2,发生了什么?
代码:
NSManagedObjectContext *c = [NSManagedObjectContext MR_contextThatPushesChangesToDefaultContext];
[c performBlockAndWait:^{
NSArray *all = [CDBaseAccount MR_findAllInContext:c];
NSLog(@"count: %d", all.count);
NSLog(@"all accounts = %@", all);
CDBaseAccount *a = [CDBaseAccount MR_createInContext:c];
a.accountName = @"foo";
[c MR_saveNestedContexts];
NSLog(@"temp a.objectID = %@", a.objectID);
NSError *error;
if (![c obtainPermanentIDsForObjects:@[a] error:&error]) {
NSLog(@"perm id error: %@", error);
return;
}
NSLog(@"perm a.objectID = %@", a.objectID);
NSURL *u = a.objectID.URIRepresentation;
dispatch_async(dispatch_get_main_queue(), ^{
NSManagedObjectContext *d = [NSManagedObjectContext MR_defaultContext];
NSArray *all = [CDBaseAccount MR_findAllInContext:d];
NSLog(@"count: %d", all.count);
NSLog(@"all accounts = %@", all);
NSManagedObjectID *i = [d.persistentStoreCoordinator managedObjectIDForURIRepresentation:u];
NSError *objWithIdError = nil;
NSManagedObject *o = [d existingObjectWithID:i error:&objWithIdError];
if (objWithIdError != nil) {
NSLog(@"existing object error: %@", objWithIdError);
return;
}
NSLog(@"o = %@", o);
NSLog(@"o.objectID = %@", o.objectID);
});
}];
控制台输出:
> +[NSManagedObjectContext(MagicalRecord) MR_contextWithStoreCoordinator:](0xa7c9b0) -> Created <NSManagedObjectContext: 0x83522a0>: Context *** MAIN THREAD ***
> count: 0
> all accounts = (
> )
> -[NSManagedObjectContext(MagicalSaves) MR_saveWithErrorCallback:](0x8353de0) -> Saving <NSManagedObjectContext: 0x8353de0>: Context *** MAIN THREAD ***
> -[NSManagedObjectContext(MagicalSaves) MR_saveWithErrorCallback:](0x8195450) -> Saving <NSManagedObjectContext: 0x8195450>: *** DEFAULT *** Context *** MAIN THREAD ***
> -[NSManagedObjectContext(MagicalSaves) MR_saveWithErrorCallback:](0x83522a0) -> Saving <NSManagedObjectContext: 0x83522a0>: *** BACKGROUND SAVE *** Context *** MAIN THREAD ***
> temp a.objectID = 0x8187ee0 <x-coredata:///CDBaseAccount/tF392AC6A-3539-4F39-AC53-35F9E5B3C9322>
> perm a.objectID = 0x8355800 <x-coredata://F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/p2>
> count: 1
> all accounts = (
"<CDBaseAccount: 0x844ca60> (entity: CDBaseAccount; id: 0x844a4c0 <x-coredata://F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/p1> ; data: <fault>)"
)
> existing object error: Error Domain=NSCocoaErrorDomain Code=133000 "The operation couldn’t be completed. (Cocoa error 133000.)" UserInfo=0x864d8c0 {NSAffectedObjectsErrorKey=(
"<CDBaseAccount: 0x864b8c0> (entity: CDBaseAccount; id: 0x86405c0 <x-coredata://F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/p2> ; data: <fault>)"
)}
答案 0 :(得分:18)
简短的回答是,不要那样做:)
启动应用程序之间 -objectID
不可靠。在以下条件下,它保证是独特和可靠的:
将-objectID
视为永久存储在持久存储之外的永久唯一标识符将会经常失败。核心数据在对象的生命周期中多次更改-objectID
的基础细节。
如果您需要外部可靠的独特产品,那么您需要自己创建一个。我通常建议将[[NSProcessInfo processInfo] globallyUniqueString]
添加到需要外部可引用唯一的实体。 -awakeFromInsert
是一个很好的选择。
答案 1 :(得分:0)
这可能是因为您正在使用嵌套上下文。
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
static NSArray *fetchAllPersons(NSManagedObjectContext *moc);
static NSManagedObjectModel *managedObjectModel();
static NSManagedObjectContext *createManagedObjectContext();
static NSURL *desktopDirectoryURL(void);
static void testObjectID(void);
int main(int argc, const char * argv[])
{
@autoreleasepool {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
testObjectID();
});
}
dispatch_main();
return 0;
}
static void testObjectID(void)
{
NSManagedObjectContext *c = createManagedObjectContext();
[c performBlock:^{
NSArray *all = fetchAllPersons(c);
NSLog(@"count: %lu", all.count);
NSLog(@"all accounts = %@", all);
NSManagedObject *a = [NSEntityDescription insertNewObjectForEntityForName:@"Person" inManagedObjectContext:c];
[a setValue:@"foo" forKey:@"name"];
NSLog(@"temp a.objectID = %@", a.objectID);
NSError *error = nil;
NSCAssert([c obtainPermanentIDsForObjects:@[a] error:&error], @"perm id error: %@", error);
NSLog(@"perm a.objectID = %@", a.objectID);
NSCAssert([c save:&error], @"Save failed: %@", error);
NSURL *u = a.objectID.URIRepresentation;
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSManagedObjectContext *d = createManagedObjectContext();
NSArray *all = fetchAllPersons(c);
NSLog(@"count: %lu", all.count);
NSLog(@"all accounts = %@", all);
NSManagedObjectID *i = [d.persistentStoreCoordinator managedObjectIDForURIRepresentation:u];
NSError *objWithIdError = nil;
NSManagedObject *o = [d existingObjectWithID:i error:&objWithIdError];
NSCAssert(o != nil, @"existing object error: %@", objWithIdError);
NSLog(@"o = %@", o);
NSLog(@"o.objectID = %@", o.objectID);
});
}];
}
static NSArray *fetchAllPersons(NSManagedObjectContext *moc)
{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
NSError *error = nil;
NSArray *result = [moc executeFetchRequest:request error:&error];
NSCAssert(result != nil, @"Fetch failed: %@", error);
return result;
}
static NSManagedObjectModel *managedObjectModel()
{
static NSManagedObjectModel *mom = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSEntityDescription *personEntity = [[NSEntityDescription alloc] init];
[personEntity setName:@"Person"];
NSAttributeDescription *nameAttribute = [[NSAttributeDescription alloc] init];
[nameAttribute setName:@"name"];
[nameAttribute setAttributeType:NSStringAttributeType];
[personEntity setProperties:@[nameAttribute]];
mom = [[NSManagedObjectModel alloc] init];
[mom setEntities:@[personEntity]];
});
return mom;
}
static NSManagedObjectContext *createManagedObjectContext()
{
static NSPersistentStoreCoordinator *coordinator;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: managedObjectModel()];
NSString *STORE_TYPE = NSSQLiteStoreType;
NSString *STORE_FILENAME = @"foobar.db";
NSError *error;
NSURL *url = [desktopDirectoryURL() URLByAppendingPathComponent:STORE_FILENAME];
NSPersistentStore *newStore = [coordinator addPersistentStoreWithType:STORE_TYPE
configuration:nil URL:url options:nil
error:&error];
if (newStore == nil) {
NSLog(@"Store Configuration Failure\n%@",
([error localizedDescription] != nil) ?
[error localizedDescription] : @"Unknown Error");
}
});
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[moc setPersistentStoreCoordinator:coordinator];
return moc;
}
static NSURL *desktopDirectoryURL(void)
{
static NSURL *URL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSError *error;
URL = [fileManager URLForDirectory:NSDesktopDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:&error];
NSCAssert(URL != nil, @"Could not access Desktop directory: %@", [error localizedDescription]);
});
return URL;
}
输出:
count: 0
all accounts = (
)
temp a.objectID = 0x10180e640 <x-coredata:///Person/tB1D48677-0152-4DA9-8573-7C7532863B4E2>
perm a.objectID = 0x101901bb0 <x-coredata://275C90E5-2598-4DFA-BF4C-E60A336E8BE4/Person/p1>
count: 1
all accounts = (
"<NSManagedObject: 0x10180e5b0> (entity: Person; id: 0x101901bb0 <x-coredata://275C90E5-2598-4DFA-BF4C-E60A336E8BE4/Person/p1> ; data: {\n name = foo;\n})"
)
o = <NSManagedObject: 0x100416530> (entity: Person; id: 0x100415b60 <x-coredata://275C90E5-2598-4DFA-BF4C-E60A336E8BE4/Person/p1> ; data: {
name = foo;
})
o.objectID = 0x100415b60 <x-coredata://275C90E5-2598-4DFA-BF4C-E60A336E8BE4/Person/p1>