2012-08-31 28 views
8

CoreDataのオブジェクトIDを扱う際に問題があります。私は便利のためにMagicalRecordを使用しています.3つのコンテキストがあります:プライベートキューの作業コンテキスト、UIの親キューコンテキストと作業コンテキストの親、およびメインコンテキストの親であるプライベートキュー保存コンテキスト。永続的なNSManagedObjectIDはあまり永久的ではありませんか?

私の目標は、作業環境でオブジェクトを作成し、永続ストアに保存し、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>)" 
)} 

答えて

18

短い答えは、

-objectIDは、アプリケーションの起動の間に信頼性がない:)ことをいたしません。治療元のオブジェクト形式で

  • (ないURLまたはNSStringの形の)アプリケーションの単一のライフサイクル内

    1. :以下の条件で、ユニークで信頼性が保証され-objectID永続的なストアの外部に格納される永続的な一意の識別子としてあなたはかなり頻繁に失敗するでしょう。コアデータはオブジェクトの寿命中に-objectIDの根本的な詳細を何度も変更します。

      外部的に信頼できるユニークなものが必要な場合は、自分で作成する必要があります。私は一般的に、外部参照可能なユニークを必要とするエンティティに[[NSProcessInfo processInfo] globallyUniqueString]を追加することをお勧めします。 -awakeFromInsertはそれを行うのに最適な場所です。

    +0

    おかげで、そんなにマーカス!すべての文書で単語「永久」という言葉は本当に欺まれています。私はあなたのCDブックの新版を出してくれることを願っています。古いものはOOPですから。 – brianpartridge

    +1

    +1の 'globallyUniqueString' –

    +1

    アプリ起動時に' isTemporaryID'に 'NO'を返す' NSManagedObjectID'があれば間違いなくスケッチをしています。これは私のために非常にうまく動作し、長い間行ってきました。 –

    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> 
    
    +0

    ええ、これは私が期待していた動作です。ネストされたコンテキストに関連している可能性があります。 – brianpartridge