永久NSManagedObjectID不是那么永久?

时间:2012-08-31 15:28:25

标签: core-data nsmanagedobject magicalrecord

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

2 个答案:

答案 0 :(得分:18)

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

启动应用程序之间

-objectID不可靠。在以下条件下,它保证是独特和可靠的:

  1. 在应用程序的单个生命周期内
  2. 以其原始对象形式(不是URL或NSString形式)
  3. -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>