Core Data es un marco que Apple proporciona a los desarrolladores y se describe como un "marco de persistencia y administración de gráficos de objetos controlados por esquemas". ¿Qué significa eso realmente? El marco gestiona dónde se almacenan los datos, cómo se almacenan, el almacenamiento en caché de datos y la gestión de la memoria. Fue portado al iPhone desde Mac OS X con la versión 3.0 iPhone SDK.
En este tutorial, lo guiaré en el proceso de creación de un proyecto con Core Data y le mostraré cómo usarlo con un simple UITableViewController. Será una simple base de datos de una tabla que almacenará los tiempos de vuelta y mostrará los tiempos y sus diferencias divididas en un UITableView.
Antes de comenzar, es importante comprender por qué es posible que desee utilizar Core Data con SQLite para el almacenamiento en listas de propiedades, un formato XML personalizado o el acceso directo a la base de datos de SQLite. La API Core Data permite a los desarrolladores crear y utilizar una base de datos relacional, realizar la validación de registros y realizar consultas utilizando condiciones sin SQL. Básicamente, le permite interactuar con SQLite en Objective-C y no tiene que preocuparse por las conexiones o la administración del esquema de la base de datos, y la mayoría de estas características son familiares para las personas que han usado tecnologías de mapeo objeto-relacional (ORM) como las implementadas en Ruby. en Rails, CakePHP, LINQ u otras bibliotecas y marcos que abstengan el acceso a la base de datos. El principal beneficio de este enfoque es que elimina el tiempo de desarrollo necesario para escribir consultas SQL complejas y manejar manualmente el SQL y la salida de esas operaciones..
La aplicación de ejemplo que construiremos hoy es un simple temporizador de vueltas. Creará un nuevo registro en el almacén de Core Data para cada vuelta que realicemos. El UITableView mostrará la diferencia entre la vuelta actual y la última..
Para empezar abriremos XCode y crearemos un nuevo proyecto. Nombra lo que desees, lo he llamado "LapTimer". Crearemos una "aplicación basada en Windows" a partir de la selección de la plantilla Nuevo proyecto. Asegúrate de que la casilla "Usar datos básicos para almacenamiento" esté marcada.
El proyecto debe ser familiar a lo que puede haber visto antes si ha desarrollado aplicaciones de iPhone anteriormente.
No hay mucha configuración para hacer ya que la opción "Usar datos básicos para el almacenamiento" en la plantilla de "Aplicación basada en Windows" ha establecido automáticamente algunas variables importantes y ha creado archivos en el proyecto para nosotros.
El archivo LapTimer.xcdatamodel es donde definiremos el esquema para nuestra base de datos SQLite. El marco de Core Data también se agregó al proyecto para incluir los archivos para el acceso a la API. Los otros cambios se realizan dentro de los archivos de aplicación predeterminados. En particular, los archivos delegados de la aplicación tienen los métodos para configurar el almacén de datos principales en nuestra aplicación y hacer referencia a ellos en los controladores UIViewControllers.
Lo que nos interesa en este momento es el archivo LapTimer.xcdatamodel en "Recursos". Este archivo le proporciona un mapa visual de su esquema que muestra las entidades y los atributos..
Hay algunos términos y frases diferentes que se usan en los Datos básicos que pueden estar relacionados de forma flexible con los nombres de bases de datos regulares, pero no son idénticos.
Una "entidad", también conocida como "objeto gestionado", es similar a una tabla. Es la definición de un objeto que contendrá una colección de datos. Un objeto de entidad contiene "atributos". Estos pueden asociarse libremente con columnas, pero estos atributos no se limitan solo al almacenamiento de datos. Los atributos pueden definir una relación entre dos entidades, una propiedad recuperada dinámicamente o definir una propiedad para el almacenamiento de datos.
A partir del diagrama anterior, puede obtener una idea de la flexibilidad de los objetos derivados de las entidades de Core Data. Para esta aplicación de ejemplo necesitaremos una entidad realmente simple. Llamaremos a la entidad "Evento" que almacenará los registros de nuestras vueltas..
Lo que haremos para crear esta entidad es hacer clic en el botón [+] en la primera columna superior (desde la izquierda). Esto creará una nueva entidad en la lista y visualmente en el mapa de esquema debajo de las columnas.
Este es un editor visual agradable para tu modelo. Core Data realmente hace el trabajo pesado cuando se trata de la parte "M" (modelo) de MVC. El editor visual muestra las relaciones, las propiedades y las entidades de la tienda, mientras que el esquema se crea y administra dinámicamente, todo para usted. Esto es similar a Interface Builder, ya que se encarga de asignar, administrar y colocar objetos para usted en una UIView sin una sola línea de código.
Con la nueva entidad de Evento en su lugar, queremos crear una "propiedad". Como esta propiedad almacenará datos, la estableceremos como un "atributo". Por lo tanto, este nuevo atributo solo almacenará la fecha actual para cuando se creó el registro. En nuestra aplicación de ejemplo, utilizaremos esto para hacer referencia a nuestros tiempos de vuelta..
La siguiente columna a la derecha es donde definimos nuestras propiedades (asegúrese de que la entidad Evento esté seleccionada). Entonces, cree una nueva propiedad usando el botón [+] en la columna, seleccione "Agregar atributo".
Llamaremos al atributo "timeStamp" y estableceremos su Tipo a "Fecha". En el menú desplegable de selección de tipo hay tipos de datos de columna similares a los disponibles en los sistemas de bases de datos relacionales como PostgreSQL o MySQL, que incluyen tipos de datos como enteros, flotadores, cadenas, booleanos, fechas y datos binarios (blobs).
Para este atributo, no necesitamos ninguna otra opción seleccionada o cambiada.
Eso es todo para el archivo xcdatamodel, y podemos continuar integrándolo en nuestra aplicación. Ahora es un buen momento para salvar tu trabajo..
Si ha utilizado un marco MVC que tiene definiciones de modelo de base de datos que definen la estructura o los comportamientos de una tabla, entonces esta será una tarea familiar.
Necesitamos comenzar creando una definición de la entidad. Hacemos esto creando una clase NSManagedObject de la entidad y definimos las variables que la tienda tiene.
Este es un proceso simple. Seleccione la entidad del evento en el archivo LapTimer.xcdatamodel y vaya a Archivo> Nuevo archivo. Verá que hay una nueva plantilla de archivo en la sección "Clase de Cocoa Touch" llamada "Clase de objetos gestionados".
Selecciona la plantilla y pulsa "Siguiente". La siguiente pantalla solo define dónde guardamos el archivo y el destino para incluirlo, todo esto es correcto de manera predeterminada, por lo tanto, presione "Siguiente" nuevamente. La siguiente pantalla es donde se definen las entidades para las que desea crear clases NSManagedObject. Si no está seleccionado, seleccione Evento y asegúrese de que las casillas de verificación "Generar accesores" y "Generar propiedades Obj-C 2.0" estén seleccionadas, no necesitaremos validaciones en este momento. Hit Finish.
Así que ahora tenemos 2 nuevos archivos en nuestra aplicación. Event.m y Event.h. Definen la clase NSManagedObject para la entidad de evento que creamos en nuestro almacén de Core Data. Definen el campo timeStamp para que cuando queramos usar la clase Evento podamos acceder al atributo.
Evento.h
#importarEvento de @interfaz: NSManagedObject @ propiedad (no atómico, retener) NSDate * timeStamp; @fin
Evento.m
#import "Event.h" @implementation Event @dynamic timeStamp; @fin
Al igual que las definiciones de modelos en otros marcos e idiomas, puede agregar métodos personalizados para todos los registros en la entidad Evento. Notará que el atributo timeStamp se ha agregado y asignado como un objeto NSDate.
Con la configuración de Core Data, ahora es el momento de trabajar en parte de la lógica del controlador en la aplicación. Usaremos un UITableViewController como la interfaz principal de la aplicación para mostrar los tiempos de las vueltas y también para registrar un nuevo tiempo..
Por lo tanto, crearemos el nuevo UITableViewController con Archivo> Nuevo archivo. Luego, en la sección de iPhone, seleccione "subclase UIViewController" y marque "subclase UITableViewController" pero no marque las casillas que se relacionan con el uso de XIB o la orientación para un iPad. No utilizaremos un XIB para este controlador. Llame al nuevo archivo "TimeTableController" y finalice el asistente de archivos.
En este controlador necesitaremos 2 propiedades, una referencia a NSManagedObjectContext y una matriz para almacenar los registros de la UITableView. Además de definir esas propiedades, importaremos el archivo Event.h para que podamos usar la clase.
TimeTableController.h
#importar#import "Event.h" @interface TimeTableController: UITableViewController NSManagedObjectContext * managedObjectContext; NSMutableArray * eventArray; @ propiedad (no atómica, retener) NSManagedObjectContext * managedObjectContext; @ propiedad (no atómica, retener) NSMutableArray * eventArray; - (nula) fetchRecords; - (void) addTime: (id) remitente; @fin
¿Qué es un NSManagedObjectContext? Se le conoce como el "bloc de notas" para Core Data en la aplicación para administrar la obtención, actualización y creación de registros en la tienda. También administra algunas características fundamentales en Core Data, incluidas las validaciones y la gestión de deshacer / rehacer para los registros..
El contexto del objeto gestionado es la conexión entre su código y el almacén de datos. Todas las operaciones que ejecutará para Core Data lo hacen contra el contexto del objeto gestionado. Cuando se realiza una solicitud, el contexto del objeto administrado hablará con el coordinador de almacenamiento persistente, que es responsable de asignar los objetos a los datos para el almacén de datos. Esto permite que los Datos Básicos sean flexibles entre los diferentes formatos del almacén de datos. Aquí hay un diagrama de cómo se ve esto..
Con el archivo de encabezado definido, necesitamos propagar las propiedades y los métodos asignados en el archivo de implementación.
TimeTableController.m
#import "TimeTableController.h" @implementation TimeTableController @synthesize managedObjectContext, eventArray; // ... // ... código comentado predeterminado de la plantilla de archivo //… - (void) viewDidLoad [super viewDidLoad]; self.title = @ "Lap Times"; UIBarButtonItem * addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem: UIBarButtonSystemItemAdd target: self action: @selector (addTime :)]; self.navigationItem.rightBarButtonItem = addButton; [addButton release]; [self fetchRecords]; - (void) addTime: (id) sender Event * event = (Event *) [NSEntityDescription insertNewObjectForEntityForName: @ "Event" inManagedObjectContext: managedObjectContext]; [evento setTimeStamp: [NSDate date]]; Error NSError *; if (! [managedObjectContext save: & error]) // Este es un error grave que indica que el registro // no se pudo guardar. Aconseje al usuario que // vuelva a intentarlo o reinicie la aplicación. [eventArray insertObject: event atIndex: 0]; [self.tableView reloadData]; - (void) fetchRecords // Defina nuestra tabla / entidad para usar NSEntityDescription * entity = [NSEntityDescription entityForName: @ "Event" inManagedObjectContext: managedObjectContext]; // Configurar la solicitud de recuperación NSFetchRequest * request = [[NSFetchRequest alloc] init]; [petición setEntity: entidad]; // Defina cómo ordenaremos los registros NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @ "timeStamp" ascendente: NO]; NSArray * sortDescriptors = [NSArray arrayWithObject: sortDescriptor]; [solicitud setSortDescriptors: sortDescriptors]; [versión sortDescriptor]; // Recupera los registros y maneja un error NSError * error; NSMutableArray * mutableFetchResults = [[managedObjectContext executeFetchRequest: error de solicitud: & error] mutableCopy]; if (! mutableFetchResults) // Manejar el error. // Esto es un error grave y debería recomendar al usuario que reinicie la aplicación // Guardar nuestros datos buscados en una matriz [self setEventArray: mutableFetchResults]; [mutableFetchResults release]; [petición de liberación]; //… //… más comentarios de plantillas y definiciones de métodos predeterminados //… - (void) dealloc [managedObjectContext release]; [eventArray release]; [super dealloc]; @final
Eso es una buena cantidad de código, así que repasemos cada método individualmente. Hay un código de cuando creas el archivo desde la plantilla, comentó métodos como viewDidUnload, que acabo de omitir de lo anterior..
Comenzamos con la llamada habitual a la súper clase. Luego definimos el título de UINavigationBar. Luego de eso necesitamos definir el botón que usaremos para agregar un registro al almacén de Core Data. Cuando se presiona el botón, le decimos que llame al selector addTime: que luego interactuará con Core Data. Después de las versiones requeridas, podemos llamar a la función fetchRecords que se explica a continuación..
Esto se activa cuando se presiona el elemento UIBarButtonItem en la parte superior derecha de la barra UINavigation. Necesitamos crear un nuevo registro de evento con el NSDate actual y guardarlo en la base de datos.
Creamos un nuevo registro de evento que se denomina NSEntityDescription. Esta es su fila en la base de datos para el nuevo registro. Para hacer esto, definimos el nombre de entidad al que pertenece el registro y proporcionamos NSManagedObjectContext. La fecha actual se configura contra el atributo timeStamp.
Luego se realiza la operación de guardar, pero hay una disposición para manejar un error si la inserción falla. Por lo general, lanzaría un UIAlertView diciendo que el registro no se creó, y quizás le indique al usuario que intente nuevamente o cierre y vuelva a abrir la aplicación..
El registro debe agregarse a la matriz desde la que se alimenta UITableView. Luego, se debe indicar a la UITableView que vuelva a cargar los datos. Puede hacer esto más gráficamente con una animación, pero por el bien del tutorial, seamos sencillos..
Este método obtendrá los datos de la tienda y los agregará a la matriz que tenemos en el controlador. Para más detalles sobre cómo obtener registros, veamos de cerca NSFetchRequest.
Core Data tiene un enfoque diferente para obtener datos de su base de datos. Es un almacén de datos NoSQL, lo que significa que todas las condiciones de una consulta se basan en métodos. Esto es excelente ya que el almacén base, que es SQLite, podría cambiarse a cualquier otra tecnología de base de datos y lo único que se cambiaría sería la conexión y los controladores de la base de datos..
Entonces, para crear una solicitud creamos un objeto NSFetchRequest. Este es el objeto base contra el que se establecerán las condiciones de consulta. Podemos definir las condiciones para hacer coincidir una propiedad particular en función de cómo se ordenan los registros. Puede encontrar más información sobre Core Data y sus condiciones para NSFetchRequests en su documentación..
Crear una nueva solicitud NSFetch es simple. Solo necesita definir la entidad de la que desea los registros y NSManagedObjectContext.
NSEntityDescription * entidad = [NSEntityDescription entityForName: @ "Event" enManagedObjectContext: managedObjectContext]; NSFetchRequest * request = [[NSFetchRequest alloc] init]; [petición setEntity: entidad];
La entidad se define utilizando un objeto NSEntityDescription que requiere el nombre de la entidad y el NSManagedObjectContext. La solicitud de recuperación se crea luego pasando la descripción de la entidad. Esto equivaldría a la primera parte de una declaración SQL:
SELECCIONAR * DE 'eventos'
En nuestra aplicación de ejemplo, ordenamos los datos por timeStamp de manera descendente. Para ello, utilizamos un NSSortDescriptor..
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey: @ "timeStamp" ascendente: NO]; NSArray * sortDescriptors = [NSArray arrayWithObject: sortDescriptor]; [solicitud setSortDescriptors: sortDescriptors]; [versión sortDescriptor];
Se crea el NSSortDescriptor y definimos el atributo que deseamos ordenar y si está ascendiendo, en este caso queremos que descienda, por lo que se establece en NO. La solicitud de recuperación puede tomar muchos descriptores de clasificación, por lo que acepta una matriz al configurar los descriptores de clasificación. Como solo queremos uno, solo tenemos que crear una matriz con un objeto en ella. Configuramos la matriz de descriptores contra la solicitud de recuperación y eso es lo que.
Para definir una condición que coincida con el contenido de un registro, la clase NSPredicate entra en juego. Permite que la solicitud de búsqueda coincida o defina un rango que el contenido de un registro debe cumplir. Este es el equivalente a tus iguales, mayor y menor que las coincidencias en SQL. Tiene más que sus funciones básicas de SQL, que puede ver aquí.
Establecer un predicado puede ser muy simple.
NSPredicate * predicate = [NSPredicate predicateWithFormat: @ "(lastName like% @) AND (birthday>% @)", lastNameSearchString, birthdaySearchDate];
Usar NSPredicate predicateWithFormat: es un método simple y familiar que le permite definir las condiciones de la consulta. Para una explicación detallada sobre NSPredicates, la documentación de Apple tiene algunas guías geniales..
Cuando haya definido las condiciones en su solicitud de recuperación, puede ejecutarla.
NSMutableArray * mutableFetchResults = [[managedObjectContext executeFetchRequest: error de solicitud: & error] mutableCopy];
Eso devolverá una matriz de objetos de entidad, NSManagedObjects, para usar en su salida de datos.
Con los datos obtenidos de los Datos del Núcleo y almacenados en el eventArray, ahora podemos generar esos registros en el UITableView.
Lo primero es decirle a la tabla que solo necesitaremos 1 sección y cuántas filas tenemos que usar.
Extracto de TimeTableController.m
-(NSInteger) numberOfSectionsInTableView: (UITableView *) tableView return 1; - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section return [eventArray count];
Si ha usado un UITableViewController anteriormente, la siguiente función debería ser sencilla.
-(UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath static NSString * CellIdentifier = @ "Cell"; static NSDateFormatter * dateFormatter = nil; if (dateFormatter == nil) dateFormatter = [[NSDateFormatter alloc] init]; [dateFormatter setDateFormat: @ "h: mm.ss a"]; UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier]; if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleValue1 reuseIdentifier: CellIdentifier] autorelease]; Event * event = [eventArray objectAtIndex: [indexPath row]]; Evento * previousEvent = nil; if ([cuenta de eventArray]> ([fila indexPath] + 1)) previousEvent = [eventArray objectAtIndex: ([fila indexPath] + 1)]; [cell.textLabel setText: [dateFormatter stringFromDate: [event timeStamp]]]; if (previousEvent) NSTimeInterval timeDifference = [[event timeStamp] timeIntervalSinceDate: [previousEvent timeStamp]]; [cell.detailTextLabel setText: [NSString stringWithFormat: @ "+%. 02f sec", TimeDifference]]; else [cell.detailTextLabel setText: @ "---"]; celda de retorno;
La celda mostrará 2 valores, a partir del uso del estilo UITableViewCellStyleValue1. La izquierda será el tiempo de la vuelta y la derecha será la diferencia en segundos con respecto al registro anterior..
Dado que este método se repite, debemos tener especial cuidado con la carga, ya que puede colocar el dispositivo si no se administra correctamente. Por ese motivo, el NSDatFormatter se almacena como una variable estática, por lo que puede reutilizarse en cada iteración sin asignarlo ni liberarlo cada vez.
La carga diferida es una técnica en la que demora la solicitud o la asignación de una propiedad tanto como sea posible. Esto ayuda a mantener la memoria baja y durante las funciones iterativas esto es primordial. Saber cuándo y cómo asignar datos es crucial para mantener una aplicación móvil rápida. La desasignación del objeto también es tan importante, cuanto antes mejor.
cellForRowAtIndexPath: es un método iterativo y todos los datos procesados o asignados en este método, especialmente, deben mantenerse al mínimo. Este método se ejecuta cada vez que se ve una celda, de modo que cuando un usuario se desplaza rápidamente, este método en particular, dependiendo del tamaño del conjunto de registros, puede llamarse muy frecuentemente en sucesión.
La siguiente tarea es obtener el objeto de evento asociado con la fila de la tabla que se debe representar. Ya que necesitamos obtener el registro anterior para la comparación de tiempo, hay una comprobación simple para ver si hay un registro anterior y almacenarlo en el evento anterior. Si existe el evento previo, calculamos la división utilizando [NSDate timeIntervalSinceDate: (NSDate)]. La textLabel y detailedTextLabel se configuran con los valores que hemos calculado.
Con la configuración del UITableViewController y la fuente de datos de la tabla trabajando con el almacén de Core Data, todo lo que se necesita es cargar el controlador cuando se inicia la aplicación.
En el controlador de la aplicación debe definirse una propiedad UINavigationController. Entonces, el método applicationDidFinishLaunching solo necesita asignar el controlador y ya está.
LapTimerAppDelegate.h
@interface LapTimerAppDelegate: NSObjectNSManagedObjectModel * managedObjectModel; NSManagedObjectContext * managedObjectContext; NSPersistentStoreCoordinator * persistentStoreCoordinator; Ventana UIWindow *; UINavigationController * navigationController; @ propiedad (no atómica, retener, solo lectura) NSManagedObjectModel * managedObjectModel; @ propiedad (no atómica, retener, solo lectura) NSManagedObjectContext * managedObjectContext; @ propiedad (no atómica, retener, solo lectura) NSPersistentStoreCoordinator * persistentStoreCoordinator; @ propiedad (no atómica, retener) IBOutlet UIWindow * window; @property (no atómico, retener) UINavigationController * navigationController; - (NSString *) applicationDocumentsDirectory; @fin
Extracto de LapTimerAppDelegate.m
#import "LapTimerAppDelegate.h" #import "TimeTableController.h" @implementation LapTimerAppDelegate @synthesize window, navigationController; - (void) applicationDidFinishLaunching: (UIApplication *) application TimeTableController * tableController = [[TimeTableController alloc] initWithStyle: UITableViewStylePlain]; tableController.managedObjectContext = [self managedObjectContext]; self.navigationController = [[UINavigationController alloc] initWithRootViewController: tableController]; [versión del controlador de mesa]; [ventana addSubview: [self.navigationController view]]; [ventana makeKeyAndVisible]; //… //… otros métodos de plantilla //… - (void) dealloc [managedObjectContext release]; [lanzamiento de managedObjectModel]; [persistentStoreCoordinator release]; [ventana de liberación]; [Liberación del controlador de navegación]; [super dealloc];
El archivo TimeTableController.h se incluye en el delegado de la aplicación y luego se asigna con un UINavigationController.
Eso debería ser. Construye la aplicación para verificar errores. Algunos de los ejemplos de código solo han sido extraídos, ninguno del código que se genera al crear un archivo se ha eliminado, solo se ha completado. Si se produce un error que no puede descifrar, puede descargar el archivo del proyecto adjunto a este tutorial. que luego puedes compilar y comparar.
Ejecutar la aplicación. Verá el controlador de navegación y el botón Agregar. Presiona el botón Agregar y obtendrás una nueva hora en la tabla..
En este tutorial, hemos creado una aplicación de ejemplo para almacenar datos simples en un almacén de Core Data. La aplicación ejecutó el procedimiento de configuración inicial al crear una aplicación con Core Data, definiendo la estructura de los datos y recuperando los registros del almacén de datos..
Esperamos que haya recibido una introducción a Core Data y pueda ver lo fácil que es usarlo y cómo puede mejorar el rendimiento y la funcionalidad de sus aplicaciones..
Si desea obtener más información sobre Core Data o si desea una visión detallada de la estructura del marco, entonces la documentación para desarrolladores de Apple es el lugar perfecto para ir..
Documentación para desarrolladores de Apple:
Introducción a la programación de datos básicos
Migración de datos de núcleo y control de versiones
Guía de programación de NSPredicate: