Core Data

Anuncio
Core Data
Luis Montesano & Ana Cristina Murillo
Core Data
●
●
●
●
Crear un grafo de objetos en memoria con
propiedades de persistencia
Proporciona un interfaz visual para crear el grafo
de objectos (tipo base de datos relacional)
Crea un mapa entre los datos persistentes y el
grafo de objetos
Permite:
–
Crear, buscar y eliminar objetos (registros de la BD)
–
Usar siempre objetos (en lugar de una API de C)
–
Usar notación tipo properties para acceder a los
datos
Core Data Stack
Core Data
●
Permite un montón de operaciones
●
Nos vamos a centrar en
–
Crear entidades, atributos y relaciones
–
Utilizar las clases de busqueda que proporciona
iOS
–
Integrarlo en un tableViewController
Core Data Visual Tool
Core Data Visual Tool
Utilizar Core Data
●
¿Como accedemos a las tablas creadas?
–
NSManagedObjectContext:
●
●
–
es el espacio de objectos en el que guardamos y
recuperamos los objetos (datos)
Juega un papel central (life cycle, unro/redo, validation...)
NSManagedObject:
●
●
●
Es la representación en objeto de un registro de la base
de datos (Entity)
Es el modelo en un MVC
Esta siempre asociado a un contexto
NSManagedObjectContext
●
La manera facil de crear y usar:
–
Se incluye al crear el proyecto soporte para
CoreData (marcar la casilla “Use core Data for
storage”)
–
En el applicationDelegate se crea
●
●
una @property llamada managedObjectContext
Código para gestionar el contexto
NSManagedObject
●
Una vez que tenemos el contexto podemos
añadir objetos al mismo
// Create and configure a new instance of the Event entity.
NSManagedObject *event = (Event *)[NSEntityDescription insertNewObjectForEntityForName:@"Event"
inManagedObjectContext:managedObjectContext];
●
El contexto es una objeto que se utilizará en
multiples controladores
–
Se debe compartir
–
Lo habitual es inicializarlos con algo tipo:
- initInManagedContext:(NSManagedObjectContext *)context;
Acceder a los datos
- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
●
Los dos métodos nos permiten recuperar o
escribir los datos de un NSManagedObject
–
La clave es el nombre de un atributo (dado en la
herramienta visual)
–
Value es lo almacenado (o lo que se va a
almacenar)
●
Son siempre objetos (incluso los números)
–
–
–
●
NSManagedObject cuando es una relación 1 a 1
NSSet para 1 a N
NSData para datos binarios
Nil cuando esta vacio
Acceder a los datos
- (id)valueForKey:(NSString *)key;
- (void)setValue:(id)value forKey:(NSString *)key;
●
●
●
El sistema se encarga de generar las sentencias de acceso a la
BD
Las busquedas se realizan de forma perezosa cuando se
accede a/escriben los datos
Necesitamos guardar los cambios
- (BOOL)save:(NSError **)errors;
- (BOOL)hasChanges;
–
Métodos de NSManagedObjectContext
–
Frecuentemente (cuando se han hecho un conjunto de cambios)
–
NSError ** para recuperar errores (si NULL, no salva nada despues de un error)
- (void)saveChangesToObjectsInMyMOC:(NSManagedObjectContext *)context {
NSError *error = nil;
if ([context hasChanges] && ![context save:&error]) {
NSLog(@“Error! %@, %@”, error, [error userInfo]);
abort(); // generates a crash log for debugging purposes
}
}
Derivando NSManagedObject
●
●
Hasta ahora hemos usado siempre objetos
genéricos
–
No hay control de ningun tipo en compilación
–
Basado en claves (cadenas) en lugar de en
@properties
Se puede crear una subclase de
NSManagedObject
–
Con properties para cada atributo
–
Xcode genera el código por nosotros
●
Dos formas: completa y por atributo
Copiar codigo para atributos
DEMO con Xcode de creación de tablas y de las
clases correspondientes
●
La clase contiene properties para cada atributo
–
●
●
Siempre objetos (NSNumber, NSData, ...)
La implementación no tiene @synthesize, sino @dynamic
–
Indica que se hará en tiempo de ejecución
–
NSManagedObject convierte en tiempo de ejecución los get/set en
valueForKey: and setValue:ForKey:
Se accede a los registros con la notación de properties
Event *myEvent = [NSEntityDescription insertNewObjectForEntityForName:@“Event”
inManagedObjectContext:managedObjectContext];];
NSNumber *myLon = event.longitude;
event.latitude = [locManager whatEverMsg: ...]
Consultar en Core Data
●
●
La entidad principal que proporciona soporte es
NSFetchRequest
Para crear una, debemos contar con:
–
NSEntityDescription: descripción de la entidad en la que
buscar (obligatorio)
–
NSPredicate: criterios para seleccionar dentro de una
entidad (opcional, por defecto busca todos)
–
NSSortDescriptors: Conjunto de funciones que indican
como ordenar los objetos encontrados (opcional, aleatorio
por defecto)
–
Configurar la busqueda: número máximo de objetos, tiempo
máximo para la busqueda (opcional, todos)
Crear un NSFetchRequest
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:@“Event” inManagedObjectContext:ctxt];
request.fetchBatchSize = 20;
request.fetchLimit = 100;
request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
request.predicate = ...;
●
Obtener la descripción de la Entity en la que vamos a buscar
●
Límites de busqueda
●
●
●
–
Tamaño del grupo de datos a recuperar (20 cada vez)
–
Número máximo de grupos
Creación de un NSArray para guardar los descriptores de
ordenación (a continuación)
Creación del predicado (a continuación)
Las NSFetchRequest se pueden guardar, se pueden generar
con parámetros que se sustituyen ...
NSSortDescriptor
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@“latitude”
ascending:YES selector:@selector(compare:)];
●
Los elementos del NSArray resultado de la
busqueda estarán ordenados de acuerdo a los
descriptores
–
pares de atributos/funciones
–
multiples pares para ordenar en varios criterios
(apellido y nombre)
–
Método por defecto compare: (en ese caso no es
obligatorio ponerlo)
NSPredicate
●
Es el Select de Core Data
●
Indica que objetos queremos recuperar con la busqueda
●
Se puede construir de varias maneras
– Con cadenas:
NSPredicate *predicate = [NSPredicate
predicateWithFormat:@"(lastName like[c] %@) AND (birthday > %@)",
lastNameSearchString, birthdaySearchDate];
●
Existen muchos tipos de expresiones
@“uniqueId == %@”, [event objectForKey:@“id”]
@“%@ in tags”, (NSManagedObject *) // tags is a to-many relationship
@“viewed > %@”, (NSDate *) // viewed is a Date attribute in the data mapping
@“name contains[c] %@”, (NSString *) // matches the string in name attribute case insensitively
–
Componiendo predicados:
NSArray *array = [NSArray arrayWithObjects:predicate1, predicate2, nil];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:array];
Ejecutar la busquda
NSFetchRequest *request = ...;
// Create the request: Entity, sort descriptors, predicate
NSError **error = nil;
NSArray *results = [mangedObjectContext executeFetchRequest:request error:&error];
NSManagedObject *event = [results objectAtIndex:0];
for (Event *event in results) { ... } // Fast enumeration
Event *event = [results lastObject]; // solo una respuesta
●
Es el NSManagedObjectContext el encargado de la
busqueda
–
Devuelve nil si hay un error
–
Devuleve los datos en un vector (vacio si no hay datos que cumplan con
el predicado)
–
Pasar NULL en error: si no se va a consultar los errores
Eliminar objetos
[managedObjectContext deleteObject:(NSManagedObject *)anObject];
●
●
Simple, pero hay que tener cuidado con las
relaciones entre objetos y como eliminar afecta
a estas relaciones (ver documentación)
Además existen multitud de otras
funcionalidades (ver documentación)
Integración en una TableView
●
●
Tenemos todo lo que necesitamos para poder rellenar una tabla
No obstante, existe una clase que simplifica este procedimiento.
NSFetchedResultsController
–
Se declara en el controlador de la vista tabla
–
Gestiona las busquedas y formatea de acuerdo a la vista tabla (número de
secciones, filas por sección, celda para cada índice...)
–
Tiene un protocolo definido (opcional)
NSFetchedResultsControllerDelegate que permite recibir notificaciones de
cambios en el contexto que afecten a la visualización
–
Tiene caché (opcional) para mantener los resultados de las busquedas
Crear un NSFetchedResultsController
●
Se crea como instancia (normalmente) del table view controller
●
Se inicializa con:
●
–
Una fetchRequest
–
Un managed object context
–
[Opcional] claves para particionar los datos en secciones (nil para una
única sección)
–
[Opcional] nombre del fichero caché (nil dehabilita la caché)
Una vez creada, la busqueda se ejecuta con performFetch:
Ejemplo
NSManagedObjectContext *context = <#Managed object context#>;
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Configure the request's entity, and optionally its predicate.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey:@"<#Sort key#>" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
[sortDescriptors release];
[sortDescriptor release];
NSFetchedResultsController *controller = [[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:context
sectionNameKeyPath:nil
cacheName:@"<#Cache name#>"];
[fetchRequest release];
NSError *error;
BOOL success = [controller performFetch:&error];
Métodos del DataSource Delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [[self.fetchedResultsController sections] count]; // Asumimos que fetchedResultsController es una propiedad
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo numberOfObjects];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = <#Get the cell#>;
NSManagedObject *managedObject = [fetchedResultsController objectAtIndexPath:indexPath];
// Configure the cell with data from the managed object.
return cell;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
id <NSFetchedResultsSectionInfo> sectionInfo = [[fetchedResultsController sections] objectAtIndex:section];
return [sectionInfo name];
}
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
return [fetchedResultsController sectionIndexTitles];
}
- (NSInteger)tableView:(UITableView *)tableView sectionForSectionIndexTitle:(NSString *)title atIndex:(NSInteger)index {
return [fetchedResultsController sectionForSectionIndexTitle:title atIndex:index];
}
Métodos del Delegate
- (void)controller:(NSFetchedResultsController *)controller
didChangeObject:(id)anObject
atIndexPath:(NSIndexPath *)indexPath
forChangeType:(NSFetchedResultsChangeType)type
newIndexPath:(NSIndexPath *)newIndexPath
{
// Inspeccionar type y actuar en consecuencia
switch(type) {
case NSFetchedResultsChangeInsert: ...
}
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
[self.tableView beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo
atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
// Inspeccionar type y actuar en consecuencia
switch(type) {
case NSFetchedResultsChangeInsert: ...
}
}
Demo en Xcode
Nos hace todo lo que hemos visto!!!
Descargar