2009-08-29 11 views
57

Estoy teniendo problemas para descifrar esto de Core Data. ¿Cómo creo una nueva entrada con una identificación única? En SQL, simplemente declararía un campo como un campo de autoincrement. No veo nada como eso aquí, pero podría estar perdiendo algo. Solo quiero un campo NSInteger incremental automático, así que cuando agregue elementos manualmente a la base de datos, tendré alguna forma de referencia sobre ellos.Creando una identificación única para un programa de Datos Core en el iPhone

Respuesta

28

No es así como funciona CoreData.

En CoreData, crea instancias de entidades. Cada instancia es única. A continuación, recupera y manipula instancias según sea necesario. CoreData se ocupa de la persistencia, incluidas las instancias de identificación única.

Aléjate de todo lo que sabes sobre las bases de datos relacionales tradicionales.

Comience here.

Luego vaya here.

Específicamente, this.

CoreData es una tecnología asombrosamente poderosa que ofrece funciones que van más allá de la simple persistencia de la base de datos. Le ahorrará muchas líneas de código y funcionará extremadamente bien si lo acepta.

+3

No estoy de acuerdo con el "paso de todo lo que sabes". Hay casos en los que se le pueden asignar entidades únicas sin propiedades distintivas (eliminar el servidor que no cumple con las reglas). Tener un autoincremento sería útil aquí. – ray

+0

¿Leyó la última oración en la pregunta de OP ("Tendré algún tipo de referencia sobre ellos")? No necesita un campo de incremento automático para eso. Los identificadores únicos de la entidad ya están integrados en los Datos principales. Sin duda, hay motivos por los que podría querer un campo de incremento automático, pero hacerlo para identificar entidades de manera única es solo reinventar la rueda. Ciertamente, puede hacerlo para la correlación de un cliente a un servidor, pero solo si el servidor no puede tomar el URI ya admitido por NSManagedObjectID. – bbum

+0

@DanBeaulieu Suspiro. Realmente me gustaría que Apple no rompa enlaces todo el tiempo. Trataré de encontrar el momento para arreglarlos. – bbum

141

Aunque estoy de acuerdo con lo que ha dicho, existe un mecanismo de identificación detrás de los datos básicos. ID están de hecho administrado por los datos básicos, pero que pueda recuperarlos con:

NSManagedObjectID *moID = [managedObject objectID]; 

Para más información ver: Core Data Programming Guide

+21

Es lamentable que el interlocutor nunca haya aceptado una respuesta tan maravillosa. – RonLugge

+0

Esto era exactamente lo que necesitaba más de 2 años después. También utilizando la presentación URIR de un NSManagedObjectID, puede almacenarlos fácilmente en NSUserDefaults como era necesario para mí en un esquema de almacenamiento en caché.^1 –

+2

Tenga en cuenta que objectID puede cambiar y lo hace, especialmente cuando se produce una migración. No confíe en que ese valor sea coherente entre un lanzamiento de la aplicación y el siguiente. - Comentario de Marcus S Zarra en otro hilo. – Mateus

2

¿Qué pasa si queremos sincronizar con una base de datos remota que sí necesita de id autoincremented para exclusivo filas de datos? ¿Hay alguna manera de implementar identificadores autoincrementados si es necesario? Entiendo que no es obligatorio para los datos centrales, pero necesito importar datos de una base de datos remota y luego subirlos nuevamente y asegurarme de que los ID estén intactos.

+1

En este caso, utilizaría dos ID y almacenaría el ID de aumento automático del servidor como un atributo del modelo de datos principal. – Tony

+0

Use lo que el lado del servidor use para identificar de manera única los registros; agregar un número de incremento automático en la parte superior seguramente funcionará (Lukasz proporciona una solución muy ineficiente), pero es solo otro detalle para romper en el futuro. – bbum

6

Eche un vistazo a: [[NSProcessInfo processInfo] globallyUniqueString].

Apple docs: identificación global para el proceso. El ID incluye el nombre de host, el ID del proceso y un sello de tiempo, lo que garantiza que el ID sea único para la red, lo cual es útil si no desea que la gente adivine el ID.

10

Esta función de clase devolverá siguiente número disponible para su propiedad de Id. (Debe ser propiedad entera).

Lo llamo auto-incrementa el generador de valores. Todavía estoy de acuerdo con otros en que hay objectID para eso, pero a veces solo necesitas el número.

Puede poner esta función en su subclase NSManagedObject para la entidad:

+(NSNumber *)nextAvailble:(NSString *)idKey forEntityName:(NSString *)entityName inContext:(NSManagedObjectContext *)context{ 


    NSFetchRequest *request = [[NSFetchRequest alloc] init]; 
    NSManagedObjectContext *moc = context; 
    NSEntityDescription *entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:moc]; 

    [request setEntity:entity]; 
    // [request setFetchLimit:1]; 

    NSArray *propertiesArray = [[NSArray alloc] initWithObjects:idKey, nil]; 
    [request setPropertiesToFetch:propertiesArray]; 
    [propertiesArray release], propertiesArray = nil; 

    NSSortDescriptor *indexSort = [[NSSortDescriptor alloc] initWithKey:idKey ascending:YES]; 
    NSArray *array = [[NSArray alloc] initWithObjects:indexSort, nil]; 
    [request setSortDescriptors:array]; 
    [array release], array = nil; 
    [indexSort release], indexSort = nil; 

    NSError *error = nil; 
    NSArray *results = [moc executeFetchRequest:request error:&error]; 
    // NSSLog(@"Autoincrement fetch results: %@", results); 
    NSManagedObject *maxIndexedObject = [results lastObject]; 
    [request release], request = nil; 
    if (error) { 
     NSLog(@"Error fetching index: %@\n%@", [error localizedDescription], [error userInfo]); 
    } 
    //NSAssert3(error == nil, @"Error fetching index: %@\n%@", [error localizedDescription], [error userInfo]); 

    NSInteger myIndex = 1; 
    if (maxIndexedObject) { 
     myIndex = [[maxIndexedObject valueForKey:idKey] integerValue] + 1; 
    } 

    return [NSNumber numberWithInteger:myIndex]; 
} 
+0

Excelente solución compañero. ¡Saludos! –

+2

Para hacerlo eficiente, ordene por índice descendente, y luego establezca el límite de búsqueda en 1. ie 'NSSortDescriptor * indexSort = [[NSSortDescriptor alloc] initWithKey: idKey ascendente: NO];' y luego descomente 'request.fetchLimit = 1; ' –

+0

¡Casi perfecto! ¡Aún mejor con la optimización de @JasonMoore! – John

6

Aquí hay tres tipos de representación de identificación único del objeto CoreData:

NSManagedObjectID *taskID = [task objectID]; 
NSURL *taskUniqueURLKey = task.objectID.URIRepresentation; 
NSString *taskUniqueStringKey = task.objectID.URIRepresentation.absoluteString; 

!!!! Nota: si Si desea utilizar la clave única anterior como índice, asegúrese de que el objeto esté guardado en contexto antes de usarlo, o el Id. de objeto sería un Id temporal, que se reemplazará más tarde (p. ej., después de guardarlo). Ver el documento niña de clase NSManagedObjectID:

- (BOOL)isTemporaryID; // indicates whether or not this ID will be replaced later, 
such as after a save operation (temporary IDs are assigned to newly inserted objects 
and replaced with permanent IDs when an object is written to a persistent store); most 
IDs return NO 
1

actualización a la respuesta @Lukasz en Swift:

static func nextAvailble(_ idKey: String, forEntityName entityName: String, inContext context: NSManagedObjectContext) -> Int { 
    let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: entityName) 
    fetchRequest.propertiesToFetch = [idKey] 
    fetchRequest.sortDescriptors = [NSSortDescriptor(key: idKey, ascending: true)] 

    do { 
     let results = try context.fetch(fetchRequest) 
     let lastObject = (results as! [NSManagedObject]).last 

     guard lastObject != nil else { 
      return 1 
     } 

     return lastObject?.value(forKey: idKey) as! Int + 1 

    } catch let error as NSError { 
     //handle error 
    } 

    return 1 
} 
0

datos Core se utiliza para el marco de persistencia, este marco abstrae la clave principal.

Cada NSManagedObject tiene su propia clave única incorporada que está disponible en su propiedad objectID.

Este id. Se usa internamente para vincular entidades sobre sus relaciones. No es necesario mantener un ID propio como clave principal como lo que solía hacer en SQL.

Siempre puede obtener la identificación mediante NSManagedObject.objectID. y obtener el contexto del objeto utilizando NSMananagedObjectContext.objectWithId

Cuestiones relacionadas