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 対 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を永続ストアの外部に保存される永続的な一意の識別子として扱うと、かなり頻繁に失敗します。Core Data-objectIDは、オブジェクトの存続期間中に何度も基本的な詳細を変更します。

外部的に信頼できるユニークが必要な場合は、自分で作成する必要があります。通常[[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 に答える