Rendimiento de datos básicos: 6 consejos que debe conocer

Escribir código de datos básicos teniendo en cuenta el rendimiento ayuda a preparar su aplicación para el futuro. Su base de datos puede ser pequeña al principio, pero puede crecer fácilmente, lo que resulta en consultas lentas y una menor experiencia para el usuario.

Desde que empecé a escribir la aplicación Collect by WeTransfer en 2017, he estado escribiendo mucho código relacionado con los datos principales, tocándolo casi todos los días. Con millones de usuarios que agregan mucho contenido, realizar código relacionado con Datos básicos se ha convertido en una habilidad importante en nuestro equipo.

Arquitectura de aplicaciones SwiftUI con MVC y MVVM Aunque puede crear una aplicación simplemente juntando un poco de código, sin las mejores prácticas y una arquitectura robusta, pronto terminará con un código espagueti inmanejable. Aprende a crear aplicaciones sólidas y mantenibles con menos errores usando esta guía gratuita.

A lo largo de los años, hemos desarrollado muchas ideas, que me complace compartir con ustedes a través de 5 consejos que deben conocer.

1: Hacer uso de un contexto de objeto administrado en segundo plano

Una cosa que no hicimos desde el principio fue hacer uso de un contexto de objeto administrado en segundo plano. Solo usamos el contexto de vista para realizar cualquier tarea relacionada con los datos principales: insertar contenido nuevo, eliminar contenido, obtener contenido, etc.

Al principio, nuestra aplicación era relativamente pequeña. Hacer solo uso del contexto de vista no fue realmente un problema y no dio lugar a ninguna penalización de rendimiento visible relacionada con los Datos Principales. Obviamente, una vez que nuestra aplicación comenzó a crecer, nos dimos cuenta de que el contexto de la vista estaba asociado con la cola principal. Las consultas lentas bloquearon nuestra interfaz de usuario y nuestra aplicación se volvió menos receptiva.

En general, la mejor práctica es realizar el procesamiento de datos en una cola en segundo plano, ya que puede ser intensivo en CPU. Ejemplos como la importación de JSON a Datos centrales podrían bloquear el contexto de la vista y provocar que la interfaz de usuario no responda.

La solución es hacer uso de un contexto de objeto administrado en segundo plano. Las API más recientes facilitan la creación de un nuevo contexto desde el contenedor persistente:

let backgroundContext = persistentContainer.newBackgroundContext()

Recomiendo este método sobre el inicializador NSManagedObjectContext(concurrenyType:), ya que se asociará automáticamente con NSPersistentStoreCoordinator y también se configurará para consumir emisiones NSManagedObjectContextDidSave. Esto mantiene el contexto de fondo sincronizado con el contexto de la vista.

Puede guardar este contexto de fondo en una subclase contenedor persistente personalizada. De esta manera, puede reutilizar su contexto de fondo y solo tiene que administrar dos contextos. Esto hace que la estructura de datos central sea fácil de entender y evita que haya múltiples contextos fuera de sincronización.

Si solo tiene que usar el contexto de fondo en unos pocos lugares, también puede decidir usar el método performBackgroundTask(_:) que crea un contexto de fondo en su lugar:

persistentContainer.performBackgroundTask { (backgroundContext) in // .. Core Data Code}

Sin embargo, este método crea un nuevo NSManagedObjectContext cada vez que se invoca. Es posible que desee considerar el uso del contexto de fondo compartido si está enviando más a menudo a un contexto de fondo.

Importante: No pase instancias de NSManagedObject entre colas

Escribir código de datos de núcleo de subprocesos múltiples es mucho más complejo que usar un contexto de vista única. La razón de esto es que no puede simplemente pasar un NSManagedObject instanciado de un contexto de vista a un contexto de fondo. Al hacerlo, se produciría un bloqueo y una posible corrupción de los datos.

Cuando es necesario mover un objeto administrado de una cola a otra, puede hacer uso del NSManagedObjectID que es seguro para subprocesos:

let managedObject = NSManagedObject(context: persistentContainer.viewContext)backgroundContext.perform { let object = try? backgroundContext.existingObject(with: managedObject.objectID)}

2: Guardar un contexto de objeto administrado solo si es necesario

Guardar un contexto de objeto administrado confirma todos los cambios actuales en el almacén principal del contexto. Como puede imaginar, esta no es una operación barata y solo se debe usar si es necesario para garantizar el rendimiento en los datos principales.

En primer lugar, es importante comprobar si hay algo que guardar. Si no hay cambios que confirmar, tampoco hay razón para realizar un guardado. Al crear un método saveIfNeeded, se permite incorporar fácilmente una comprobación de esto:

Considere cuidadosamente cuándo guardar sus cambios

Además de usar saveIfNeeded en lugar de save(), también debe considerar si un guardado tiene sentido. Aunque un contexto podría tener cambios, no siempre es necesario confirmar directamente estos cambios.

Por ejemplo, si tiene varios elementos importantes en su base de datos, es posible que solo desee guardarlos después de haber importado todos los elementos en su contexto de fondo. A menudo, un guardado va seguido de actualizaciones de la interfaz de usuario y varios guardados uno tras otro podrían resultar fácilmente en recargas innecesarias. Además de eso, tenga en cuenta que los cambios guardados en un contexto de fondo se fusionan en el contexto de vista, bloqueando la cola principal en breve también. Por lo tanto, ¡sé consciente!

3: Obtener solo lo que necesita

Obtener datos es una tarea costosa y debe tener el máximo rendimiento posible para que su aplicación esté preparada para grandes conjuntos de datos. El siguiente código es un error a menudo cometido:

Este código cargará todos los objetos insertados en la memoria mientras se filtra directamente después para permanecer solo con contenido que tenga un nombre.

Es mucho más eficiente usar predicados para recuperar solo los objetos que se necesitan. El filtro anterior se puede escribir como sigue con un NSPredicate:

Esto tiene dos ventajas:

  • Solo se cargan en la memoria los objetos necesarios
  • No es necesario iterar sobre todos los objetos

Los predicados son muy flexibles y deberían permitirle obtener el conjunto de datos deseado en la mayoría de los casos mientras mantiene el rendimiento en los datos principales.

4: Hacer uso de los límites de búsqueda

Siguiendo el ejemplo anterior, es importante establecer límites de búsqueda cuando solo va a mostrar una parte del conjunto de datos.

Por ejemplo, supongamos que solo necesita los primeros 3 nombres de todos los elementos de contenido. En este caso, no sería necesario cargar en memoria todos los elementos de contenido que tengan un nombre. Podríamos evitar esto estableciendo un límite de búsqueda:

Este código solo devolverá los primeros 3 elementos de contenido que tengan un nombre.

5: Elimine muchos objetos a la vez utilizando un NSBatchDeleteRequest

En lugar de iterar sobre un conjunto de datos eliminando cada objeto uno por uno, a menudo es más eficiente usar un NSBatchDeleteRequest que se ejecuta más rápido, ya que opera a nivel SQL en el propio almacén persistente.

Puede obtener más información sobre las solicitudes de eliminación por lotes en mi publicación de blog Utilizando NSBatchDeleteRequest para eliminar lotes en los datos principales.

6: Sepa cómo depurar el código de datos del núcleo

Al igual que con todo el código que escribe, es importante saber cómo optimizarlo y depurar una vez que no funciona como se espera. Hay muchas formas de depuración que se explican mejor en mi entrada de blog dedicada: Depuración de datos centrales en Xcode utilizando argumentos de lanzamiento.

Arquitectura de aplicaciones SwiftUI con MVC y MVVM Aunque puede crear una aplicación simplemente juntando un poco de código, sin las mejores prácticas y una arquitectura robusta, pronto terminará con un código espagueti inmanejable. Aprende a crear aplicaciones sólidas y mantenibles con menos errores usando esta guía gratuita.

Conclusión

Escribir código de datos principales de rendimiento desde el principio le ayuda a preparar su aplicación para futuros conjuntos de datos grandes. Aunque es posible que tu aplicación funcione al principio, puede ralentizarse fácilmente una vez que tu base de datos y modelo crezcan. Al hacer uso de un contexto en segundo plano, solicitudes de recuperación inteligente y solicitudes de eliminación por lotes, está haciendo que su código de datos principales ya sea más eficiente.

Si desea mejorar aún más sus conocimientos sobre Swift, consulte la página de categorías de Swift. No dude en ponerse en contacto conmigo o twittearme en Twitter si tiene algún consejo o comentario adicional.

Gracias!



+