2010-08-15 25 views
14

Mi aplicación tiene un UISearchBar que permite al usuario ingresar palabras clave de búsqueda. Cada pulsación de tecla ejecuta una consulta de Datos centrales para mostrar los resultados como texto en los cambios de la barra de búsqueda.¿Cómo mejorar el rendimiento de Core Data?

El problema es que las pulsaciones de teclas de la barra de búsqueda se están quedando bastante mal ... seguramente debido a la lenta búsqueda. ¿Alguna idea de cómo mejorar el rendimiento?

My Core Data está respaldado con el almacén de datos sqlite que contiene 1000 objetos.

// searchKeyword is the string appears in UISearchBar 
// Both title and author may contain several words so I can't use BEGINSWITH 
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"(author CONTAINS[c] %@) OR (title CONTAINS[c] %@)", searchKeyword, searchKeyword]; 

NSEntityDescription* entity = [NSEntityDescription entityForName:@"Book" inManagedObjectContext:managedObjectContext]; 

NSFetchRequest* request = [[NSFetchRequest alloc] init]; 
[request setEntity:entity]; 
[request setPredicate:predicate]; 
[request setFetchLimit:10]; 

NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"title" ascending:YES]; 
NSArray* sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil]; 
[request setSortDescriptors:sortDescriptors]; 

[sortDescriptor release]; 
[sortDescriptors release]; 

execute request and fetch the results 
fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request 
           managedObjectContext:managedObjectContext 
           sectionNameKeyPath:nil 
           cacheName:nil]; 
NSError* error = nil; 
BOOL success = [fetchedResultsController performFetch:&error]; 
[request release]; 

Respuesta

18

El uso de CONTAINS lo ralentiza. Lo que necesita hacer es crear una nueva tabla llamada searchWords (o lo que sea), y en esa tienda todas las palabras dentro de sus títulos, hechas en minúsculas y con acentos eliminados. Estos tienen una relación que los vincula de nuevo a los objetos originales. Asegúrese de que el campo palabra esté indexado.

Realice su consulta en esta tabla, pero en vez de usar contiene o BEGINSWITH, hacer algo como

palabra> "término" y la palabra < "golondrina"

Tenga en cuenta que en la primera cadena no es la término de búsqueda, y el segundo es el término de búsqueda con el último carácter incrementado. Esto permite que Core Data use el índice SQL para realizar la búsqueda.

Apple tiene una sesión WWDC Core Data que explica esto, incluido el código de muestra. El código de ejemplo contiene una clase que maneja la normalización de la cadena (es decir, eliminación de mayúsculas y minúsculas) y el incremento del último carácter de una palabra de una manera consciente de Unicode.

+0

¡Esta es una gran solución! ¡¡Gracias!! Buscaré esa sesión y la publicaré aquí si la encuentro. – Joshua

+1

Enlace directo: http://developer.apple.com/itunes/?destination=adc.apple.com.4092349126.04109539109.4144345613?i=2041584141 Hmm .. Esperé mucho tiempo para su publicación. Mientras tanto, me encontré a mí mismo, luchando por Internet: D Está en "WWDC 2010, Optimización del rendimiento de datos principales en iPhone OS" a las 35:06 –

+1

Lo siento, no pude encontrar el código de muestra relacionado (y soy un desarrollador registrado) . ¿Podría proporcionar un código corto cortado? (Normalización e incremento de char) – obiwahn

2

Se puede crear un trie forma incremental con el fin de tener un índice de consultas anteriores (conjuntos de resultados se señalan por los nodos hoja). pero no aumentará el rendimiento de una sola consulta. También puede modificar el sistema de pulsaciones de teclas, no realizar una búsqueda después de cada pulsación de tecla, pero solo después de una pulsación de teclado después de un intervalo de tiempo (como umbral) antes de que se reconozca otra pulsación

4

Si bien no se acelerará la consulta, al colocar la búsqueda en un hilo de fondo, detendrá las pulsaciones del teclado.

+1

+1 Pero recuerde ejecutar solo una consulta de fondo a la vez, y obtener la cadena de búsqueda más reciente en cada consulta (o unir las actualizaciones de campo de texto) y disminuir la prioridad de hilo/NSOperation hasta que la UI funcione decentemente. –

+0

Gracias por los consejos.Realmente estoy tratando de evitar la solución de múltiples hilos, pero lo haré si no tengo otra opción. – Joshua

-1

Al iniciar la aplicación, cree un trie completo y adáptela al editar. No uses ese predicado estúpido. Solo está obteniendo 1000 registros, por lo que no debería tardar.

+0

Lo siento, no estoy seguro de cómo puedo crear un trie completo en el inicio de la aplicación. ¿Puedo realmente construir tal título/autor trie de antemano? – Joshua

+0

Basta con compilarlo en - aplicación (BOOL): aplicación (UIApplication *) didFinishLaunchingWithOptions: (NSDictionary *) launchOptions –

+0

es mejor no compilarlo por completo sino incrementalmente como sugerí en mi respuesta. – rano

13

Si bien la recomendación de Amoyra es correcta, también tiene un problema de diseño.

Solo la primera letra escrita en el campo de búsqueda debería alguna vez golpear el disco. Después de la primera letra, solo debería refinar los resultados de búsqueda de lo que ya está en la memoria.

Debe filtrar la matriz que está en la memoria (desde la primera búsqueda de letras) utilizando el predicado que ya tiene y evitar ejecutar una solicitud de búsqueda ya que no es necesario.

Además, si va a buscar datos que ya están en la memoria (como vivir en un NSFetchedResultsController), toda su búsqueda solo debe golpear los objetos en la memoria. Salir al disco es innecesario y terriblemente dispendioso.

+1

Hola Marcus, este es un punto muy interesante. Pero creo que hay un problema con esa solución. Al hacer clic en 'a' o 'e' como la primera letra, probablemente traerá la mayoría de los objetos de la base de datos (si no todos). Cuando se trata de una base de datos grande, esta búsqueda simple puede llevar mucho tiempo sin hablar de las implicaciones de la memoria ... – Joshua

+0

Esa situación no cambiaría independientemente de la implementación que utilice. Core Data extraerá esqueletos para que el uso de la memoria sea lo más bajo posible. A medida que filtra ese uso de la memoria se reduciría. La solución definitivamente funciona como lo he usado muchas veces. –

+0

@ MarcusS.Zarra Buen punto, Marcus, pero ¿cómo se hace esto con un NSFetchedResultsController? La propiedad 'fetchedObjects' es de solo lectura ... – Mundi