11

我在处理 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>)"
  )}
4

2 回答 2

21

简短的回答是,不要那样做:)

-objectID在您的应用程序启动之间不可靠。在以下条件下保证其唯一性和可靠性:

  1. 在应用程序的单个生命周期内
  2. 以其原始对象形式(不是 URL 或 NSString 形式)

-objectID视为要存储在持久存储之外的永久唯一标识符会经常让您失败。-objectIDCore Data在对象的生命周期中多次更改底层细节。

如果您需要一个外部可靠的唯一性,那么您需要自己创建一个。我通常建议将 a 添加[[NSProcessInfo processInfo] globallyUniqueString]到任何需要外部引用的唯一实体。 -awakeFromInsert是这样做的好地方。

于 2012-08-31T15:38:05.860 回答
1

这可能是因为您使用的是嵌套上下文。

#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>
于 2012-09-01T13:56:03.770 回答