2012-08-31 16 views
8

Tengo problemas para tratar las ID de objeto en CoreData. Estoy usando MagicalRecord para mayor comodidad y tengo 3 contextos: un contexto de trabajo de cola privada, un contexto de cola principal para UI y parent en el contexto de trabajo, y un contexto de ahorro de cola privada que es el padre del contexto principal.NSManagedObjectID permanente no tan permanente?

Mi objetivo es crear un objeto en el contexto de trabajo, guardarlo en la tienda persistente, guardarlo en la URL de objectID a NSUserDefaults, y luego poder extraer esa MO usando el ID de objeto más adelante. Sin embargo, lo que estoy descubriendo es que después de guardar la identificación permanente del objeto está cambiando.

En la salida de la consola a continuación se puede ver que después de solicitar la identificación permanente el valor que se obtiene es "F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/p1", pero más tarde, cuando enumero todos los objetos en CD el único objeto allí tiene el ID "F474F6EE-A225-456B-92EF-AB1407336F15/CDBaseAccount/p2". p1 vs p2, ¿qué pasó?

Código:

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

     }); 
    }]; 

salida de la consola:

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

Respuesta

18

respuesta corta es, no haga eso :)

-objectID no es fiable entre los lanzamientos de su aplicación. Se garantiza que sea único y fiable bajo las siguientes condiciones:

  1. Dentro de un único ciclo de vida de la aplicación
  2. En su forma objeto original (no en el URL o la forma NSString)

El tratamiento de la -objectID como un identificador único permanente que se almacenará fuera de la tienda persistente va a fallar con bastante frecuencia. Core Data cambia los detalles subyacentes del -objectID muchas veces durante la vida útil del objeto.

Si necesita una pieza externa confiable única, entonces necesita crear una usted mismo. En general, recomiendo agregar un [[NSProcessInfo processInfo] globallyUniqueString] a cualquier entidad que necesite un único externo con capacidad de referencia. -awakeFromInsert es un gran lugar para hacer eso.

+0

Gracias, tanto Marcus! El término "permanente" en toda la documentación es realmente engañoso. Realmente espero que publiques una nueva edición de tu libro de CD, ya que el anterior es OOP. – brianpartridge

+1

+1 para 'globallyUniqueString' –

+1

Definitivamente está haciendo algo incompleto si un' NSManagedObjectID' que devuelve 'NO' a' isTemporaryID' cambia entre la aplicación iniciada. Esto funciona muy bien para mí y lo ha hecho durante mucho tiempo. –

0

Esto puede deberse a que está utilizando contexto anidado.

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

Salidas:

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í, este es el comportamiento que esperaba. Quizás esté relacionado con contextos anidados. – brianpartridge

Cuestiones relacionadas