Advertisement
  1. Code
  2. Core Data

¿Qué es un fallo en los datos básicos?

by
Read Time:11 minsLanguages:

Spanish (Español) translation by Esther (you can also view the original English article)

Los fallos son un componente esencial de los datos básicos. Aunque el término suene siniestro, los fallos son inherentes al ciclo de vida de un registro de Datos Básicos. En este tutorial, aprenderás qué son los fallos, cómo manejarlos y cómo depurar los problemas relacionados con los fallos.

Requisitos previos

Core Data es un tema avanzado así que voy a asumir que ya estás familiarizado con Xcode y el desarrollo de iOS. Aunque voy a utilizar el lenguaje de programación Swift para este tutorial, todo en este tutorial es también aplicable a Objective-C.

¿Qué es una falla?

Core Data es muy bueno en lo que hace gracias al duro trabajo del equipo de Core Data de Apple. Core Data está altamente optimizado para mantener su huella de memoria baja sin sacrificar el rendimiento. Los fallos son una de las técnicas que utiliza Core Data para consumir la menor cantidad de memoria posible.

Los fallos no son exclusivos de Core Data. Una técnica similar se utiliza en muchos otros frameworks, como Ember y Ruby on Rails. La idea es simple, solo se cargan los datos cuando se necesitan. Para hacer que los fallos funcionen, Core Data hace un poco de magia bajo el capó creando subclases personalizadas en tiempo de compilación que representan los fallos. Veamos cómo funciona con un ejemplo.

He creado una sencilla aplicación de ejemplo para trabajar con ella. Descarga el proyecto Xcode de GitHub y ábrelo en Xcode. El proyecto utiliza Swift 2.1, lo que significa que necesitas Xcode 7.1 o superior para satisfacer el compilador.

El modelo de datos contiene dos entidades, Lista y Artículo. Una lista puede tener cero o más elementos y un elemento siempre está vinculado a una lista. Es un ejemplo clásico de relación de uno a muchos.

Data ModelData ModelData Model

Ejecuta la aplicación y rellena el almacén persistente, una base de datos SQLite, con algunos datos creando algunas listas y elementos. Sal de la aplicación cuando hayas terminado.

Fallos de disparo

Ahora deberías tener una aplicación con algunos datos. Veamos cómo funcionan los fallos en la práctica añadiendo algunas sentencias print. Abre ListsViewController.swift y busca prepareForSegue(_:sender:). En este método, obtenemos la lista que el usuario ha seleccionado en la vista de tabla. Descomenta las declaraciones de impresión en prepareForSegue(_:sender:) como se muestra en la implementación de abajo.

Ejecuta la aplicación y toca una de las listas en el controlador de la vista de listas. El ejemplo solo es interesante si se toca una lista que tiene uno o más elementos asociados. Inspeccionemos la salida de las sentencias print paso a paso.

Lo primero que hay que tener en cuenta es el nombre de la clase, Faulting.List. Esto es lo que esperamos ya que el módulo se llama Faulting y la clase se llama List. En Swift, el nombre completo de una clase se compone del nombre del módulo y del nombre de la clase.

La salida en la consola también muestra el nombre de la entidad, List, y un diccionario de datos. El atributo name se establece en List 6 mientras que el atributo items se marca como un fallo de relación. Este es el primer tipo de fallo en los datos básicos. Core Data entiende que no es necesario cargar la relación. En cambio, marca la relación como una falta. La segunda declaración de impresión lo confirma, como puedes ver a continuación.

Sin embargo, la tercera declaración impresa es más interesante. Imprime el número de elementos asociados a la lista.

Core Data solo puede hacer esto disparando el fallo de la relación. Solicita al almacén persistente los elementos asociados a la lista. Sin embargo, esto es solo una parte de la historia, como lo ilustra la cuarta declaración impresa.

Ya no vemos un fallo en la relación, pero seguimos viendo un fallo. ¿De qué se trata? Core Data solo puede darnos el número de elementos de la lista disparando o resolviendo el fallo de la relación. Sin embargo, esto no significa que Core Data resuelva los elementos de la relación. La salida en la consola lo confirma.

Podemos ver que los registros de los artículos están ahí, incluyendo el identificador que Core Data utiliza internamente. El diccionario de datos, sin embargo, está marcado como un fallo. De nuevo, Core Data solo nos da lo que pedimos. Afortunadamente, los detalles más importantes los maneja Core Data.

Profundicemos un poco más y busquemos uno de los elementos de la lista. Lo hacemos llamando a anyObject() sobre el objeto items. Imprimimos el elemento resultante en la quinta sentencia print.

El resultado no debería sorprenderte. La salida confirma que se trata de una entidad Item. No es de extrañar que el diccionario de datos siga marcado como un fallo. En la sexta sentencia print, imprimimos el atributo name del elemento.

Como pedimos el valor de un atributo del registro, Core Data lanza el fallo para darnos ese valor. Obtiene los datos del artículo y rellena el diccionario de datos. La séptima declaración impresa confirma estos resultados.

El diccionario de datos contiene el atributo name así como la relación list. La octava y última sentencia de impresión muestra que el fallo de relación del objeto list está resuelto.

No se puede cumplir con un fallo

He decidido escribir este artículo para explicar un problema con el que muchos desarrolladores que utilizan Core Data se encuentran en un momento u otro, disparando un fallo que no se puede cumplir. El problema es sencillo. Supongamos que tienes una lista con un número de elementos y, en algún momento, el usuario borra la lista. ¿Qué ocurre con los elementos de esa lista? ¿Qué ocurre si Core Data intenta disparar la relación list de uno de los elementos que pertenecían a esa lista? Descubrámoslo.

Volvamos a ver el proyecto que has descargado de GitHub. Abre Faulting.xcdatamodeld, el modelo de datos del proyecto. Selecciona la relación items de la entidad List y abre el Data Model Inspector a la derecha. Lo que nos interesa es la Delete Rule, que actualmente está configurada en Cascade. Esto significa que cada elemento de la lista se elimina cuando se borra la lista. Esto tiene sentido ya que no queremos tener artículos abandonados que no estén asociados a una lista.

Delete Rule CascadeDelete Rule CascadeDelete Rule Cascade

Selecciona la relación list de la entidad Item y abre el Data Model Inspector. La Delete Rule de esta relación se establece como Nullify. Esto significa que el destino de la relación, el objeto de la lista, se establece como nulo cuando el registro de destino, el artículo, se elimina. Esta es la regla de eliminación por defecto para una relación.

Delete Rule NullifyDelete Rule NullifyDelete Rule Nullify

Ejecuta la aplicación, crea unas cuantas listas y elementos, y toca el botón Items en la parte superior izquierda para mostrar todos los elementos que has creado. Pulsa Cancelar en la parte superior izquierda, elimina una de las listas y vuelve a pulsar el botón Items para ver qué ha cambiado. Como era de esperar, los elementos asociados a la lista eliminada también han sido borrados por Core Data. Esto no es magia. Core Data simplemente ejecuta las reglas de eliminación que definimos en el modelo de datos.

Podemos concluir que las relaciones están configuradas correctamente para este tipo de aplicación. Pero qué ocurre si no configuramos correctamente las reglas de borrado. Abre el modelo de datos y establece la regla de eliminación de ambas relaciones, items y list, a No Action. Inicia de nuevo la aplicación y crea algunas listas y elementos. Si eliminas una lista y tocas el botón Items en la parte superior izquierda para ver todos los elementos del almacén persistente, los elementos asociados a la lista eliminada deberían seguir estando ahí. No se han borrado aunque se haya eliminado la lista a la que pertenecen.

Toca una de las listas y mira lo que ocurre. Si estás ejecutando la aplicación en iOS 8, la aplicación se bloqueará debido a una excepción no capturada. Si estás ejecutando la aplicación en iOS 9, entonces solo verás un error en la consola. Deja de rascarte la cabeza y resolvamos esto juntos.

Object Inaccessible

Aunque la aplicación se bloquea en iOS 8, la salida que vemos en la consola es clara y directa.

Lo primero que hay que notar es que la aplicación se estrelló debido a una excepción no capturada. Sin embargo, lo más importante para nuestra discusión es la razón por la que se lanzó la excepción. Core Data nos dice que no ha podido cumplir con una falla para una relación. La relación a la que se refiere Core Data es la relación list del elemento que tocamos en la vista de tabla.

Si miras la implementación de tableView(tableView:didSelectRowAtIndexPath:), entenderás por qué Core Data lanzó una excepción. En este método, obtenemos el elemento que el usuario ha tocado e imprimimos el nombre de la lista en la consola.

Dado que la relación de list es un fallo, Core Data necesita disparar el fallo para resolverlo. Esto significa que Core Data pide al almacén persistente el registro de la lista. El problema es que el registro ya no está en el almacén persistente, por lo que se lanzó una excepción.

Borrar fallos inaccesibles

Hasta iOS 9, este ha sido siempre el comportamiento por defecto. A partir de iOS 9, Core Data ya no lanza una excepción. En su lugar, registra un mensaje críptico en la consola. A pesar de que el mensaje es poco claro, sigue conteniendo la razón del problema.

Lo esencial es que Core Data ya no lanza una excepción cuando no puede cumplir un fallo. La buena noticia es que tu aplicación ya no se bloquea si no capturas la excepción.

La razón de que no se lance una excepción en iOS 9 se debe a la introducción de una nueva propiedad, shouldDeleteInaccessibleFaults, y un nuevo método, shouldHandleInaccessibleFault(_:forObjectID:triggeredByProperty:), en la clase NSManagedObjectContext. No voy a cubrir estas adiciones en este tutorial.

Lo que tienes que recordar es que iOS 9, por defecto, establece el shouldDeleteInaccessibleFaults a true. Esto significa que un objeto gestionado inaccesible se marca como eliminado y sus propiedades se ponen a cero. Si shouldDeleteInaccessibleFaults es false, Core Data vuelve al comportamiento anterior lanzando una excepción.

Por supuesto, la propiedad shouldDeleteInaccessibleFaults va de la mano con shouldHandleInaccessibleFault(_:forObjectID:triggeredByProperty:). Si anulas este método, puedes manejar los objetos inaccesibles de forma más elegante.

Tratamiento de los fallos

Cuando encuentres una excepción debido a que Core Data no puede cumplir con un fallo, puedes pensar que Core Data está siendo un poco agresivo. Esta reacción es bastante común para las personas nuevas en el marco. La verdad es que la culpa es del promotor. Core Data se diseñó con un conjunto específico de objetivos en mente, y las fallas son un componente esencial para alcanzar estos objetivos.

A pesar de su nombre, las faltas deben ser siempre realizables. Si no se puede cumplir un fallo, significa que el modelo de datos no está configurado correctamente o que la aplicación no respeta las normas establecidas por el marco de datos básicos.

En la Guía de programación de Core Data, Apple enumera una serie de escenarios que pueden dar lugar a fallos que ya no se pueden cumplir. Los escenarios más comunes son las relaciones mal configuradas (reglas de eliminación) y la eliminación de un objeto gestionado mientras la aplicación todavía tiene una referencia fuerte a ese objeto gestionado.

Atención, hay otros escenarios posibles. Según mi experiencia, los desarrolladores suelen encontrarse con problemas similares debido a un problema con la pila de Core Data. Por ejemplo, si la aplicación mantiene una referencia fuerte a un objeto gestionado y, en algún momento, retira el contexto de objeto gestionado de ese objeto gestionado, entonces Core Data ya no puede cumplir ningún fallo para ese objeto gestionado. Espero que entiendas que esto no debería ocurrir si juegas con las reglas de Core Data.

Conclusión

Los fallos de Core Data son increíblemente útiles y un componente clave del marco de persistencia de Apple. Espero que este artículo te haya enseñado a lidiar con las faltas y dónde buscar si te encuentras con faltas que no se pueden cumplir. Si tienes alguna pregunta, no dudes en dejarla en los comentarios a continuación o ponte en contacto conmigo en Twitter.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.