2012-08-31 14 views
8

Ho problemi con gli ID oggetto in CoreData. Sto usando MagicalRecord per comodità e ho 3 contesti: un contesto di lavoro in coda privata, un contesto di coda principale per l'interfaccia utente e genitore per il contesto di lavoro, e un contesto di salvataggio della coda privata che è il genitore del contesto principale.NSManagedObjectID permanente non è così permanente?

Il mio obiettivo è creare un oggetto nel contesto di lavoro, salvare nell'archivio permanente, salvare il suo URL ID oggetto in NSUserDefaults e quindi essere in grado di estrarre tale MO utilizzando l'oggettoid in seguito. Tuttavia, quello che sto trovando è che dopo aver salvato l'ID permanente dell'oggetto sta cambiando.

Nella output della console di seguito si può vedere che dopo che richiedo l'ID permanente il valore di torno è "F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/P1", ma più tardi, quando ho elencare tutti gli oggetti in CD l'unico oggetto ha l'ID "F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/p2". p1 vs p2, cosa è successo?

Codice:

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); 

     }); 
    }]; 

uscita della console:

> +[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>)" 
)} 

risposta

18

risposta breve è, non farlo :)

-objectID non è affidabile tra i lanci della vostra applicazione. È garantito per essere unico e affidabile nelle seguenti condizioni:

  1. Da un singolo ciclo di vita dell'applicazione
  2. Nella sua forma oggetto originale (non in URL o forma NSString)

Trattando la -objectID come identificatore univoco permanente da conservare al di fuori dell'archivio persistente sta per fallire abbastanza spesso. Dati principali modifica i dettagli sottostanti di -objectID molte volte durante la vita dell'oggetto.

Se è necessario un esclusivo esternamente affidabile, è necessario crearne uno da soli. In genere raccomando di aggiungere uno [[NSProcessInfo processInfo] globallyUniqueString] a qualsiasi entità che abbia bisogno di un unico esternamente referibile. -awakeFromInsert è un ottimo posto per farlo.

+0

Grazie, tanto Marcus! Il termine "permanente" in tutta la documentazione è davvero ingannevole. Spero davvero che tu abbia pubblicato una nuova edizione del tuo CD book, dal momento che quello vecchio è OOP. – brianpartridge

+1

+1 per 'globallyUniqueString' –

+1

Stai sicuramente facendo qualcosa di approssimativo se un' NSManagedObjectID' che restituisce 'NO' a' isTemporaryID' cambia tra l'app avviata. Questo funziona molto bene per me e lo ha fatto per molto tempo. –

0

Ciò potrebbe essere dovuto al fatto che si utilizza il contesto nidificato.

#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; 
} 

Uscite:

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

Sì, questo è il comportamento che mi aspettavo. Forse è correlato a contesti nidificati. – brianpartridge

Problemi correlati