2012-07-15 13 views
6

Estoy desarrollando una aplicación para iPad y actualmente estoy luchando para encontrar el mejor enfoque para el multihilo. Permítanme ilustrar esto con un ejemplo simplificado:
Tengo una vista con 2 subvistas, un selector de directorio y una galería con miniaturas de todas las imágenes en el directorio seleccionado. Debido a que la 'descarga' y la generación de estas miniaturas puede llevar bastante tiempo, necesito multithreading para que la interacción y la actualización de la vista no se bloqueen.¿Cuál es el mejor método de subprocesamiento en el objetivo C?

Esto es lo que ya intentado:
[auto performSelectorInBackground: @selector (displayThumbnails :) withObject: CurrentFolder];
Esto funcionó bien porque las interacciones de los usuarios no se bloquearon, sin embargo falla de manera lamentable cuando el usuario toca otra carpeta mientras la primera carpeta aún se está cargando. Dos hilos intentan acceder a la misma vista y a las mismas variables, lo que da como resultado un error en la correcta ejecución de los demás. Cuando los usuarios tocan otra carpeta, el displayThumbnails de la carpeta que se está cargando se abortará. No he encontrado ninguna manera de hacer esto ..

NSThreads
He intentado esto, pero tuvo problemas con casi los mismos problemas que con el primer método, no he encontrado una manera (fácil) para cancelar la método continuo. (Sí, sé de [aThread cancel] pero no encontré la manera de 'reanudar' el hilo). Tal vez debería subclase NSThread e implementar mis propios métodos isRunning, etc. Pero, ¿hay alguna otra forma mejor o una tercera (o incluso cuarta y quinta) opción que estoy pasando por alto?

Creo que este es un ejemplo bastante simple y creo que hay tal vez una mejor solución sin la subclasificación NSThread. ¿Entonces, qué harías? Sus opiniones por favor!

+4

Aparentemente no puedo dar una respuesta que solo dice "GCD" – JustSid

+4

Estás completamente equivocado al respecto. Seriamente. – puzzle

+0

Ver [aquí] (https://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html%23//apple_ref/doc/uid/TP40008091-CH102-SW2) por qué GCD es tan genial :) O mejor aún, mira una de las sesiones WWDC :) Acerca de tu problema original: hay varias maneras de cancelar o pausar, sin importar si estás usando hilos, GCD o NSOperationQueue. Puede dormir, suspender, esperar en un bloqueo, cancelar por completo, etc. Creo que NSOperationQueue será una buena solución para usted en este momento, ya que lo guía automáticamente a una implementación razonable. – puzzle

Respuesta

6

NSOperationQueue debería funcionar bien para esta tarea.

Otra opción sería simple GCD, sin embargo, si nunca ha trabajado con ella, NSOperationQueue es probablemente la mejor opción, ya que prácticamente lo guía a implementar las cosas "de la manera correcta", tiene formas obvias de cancelación, etc.

5

Desea utilizar NSOperations concurrentes para descargar y procesar imágenes en segundo plano. Estos serían gestionados por un NSOperationsQueue. Básicamente, estas operaciones se configurarán para buscar una imagen por operación, procesarla, guardarla en el sistema de archivos y luego devolver el mensaje a la aplicación principal en el hilo principal donde está disponible la imagen.

Hay varios proyectos en github que puede ver que muestran cómo hacerlo - simplemente busque github usando "Concurrent" o "NSOperation".

iOS tiene una muy buena facilidad para hacer trabajos de fondo. Grand Central Dispatch (GCD) y Blocks, pero esos no le permiten tener un objeto usando devoluciones de llamadas de delegados, por lo tanto, NSOperation.

Por lo tanto, debe leer en bloques, GCD, y luego ver algún código de fuentes concurrentes NSOperations. El uso simultáneo de NSOperations no es tan simple como usar bloques.

0

Si tuve este problema, probablemente me vaya para un enfoque como este:

  • un solo hilo que va a cargar las imágenes, y hace que el hilo principal para mostrar los resultados (no soy una gran fan de tener problemas con los objetos de la GUI)

  • cuando se solicita un nuevo directorio ... bueno, depende de cómo quiera administrar las cosas. Básicamente, una construcción de cola estándar (variable de condición y matriz) podría usarse para que el hilo principal diga al hilo que "se necesitará este directorio" pasándole el nombre de ruta; el hilo comprobará la cola incluso cuando está cargando imágenes (como después de cada imagen) y cambiará al nuevo directorio cada vez que se muestre

  • podría hacer un objeto de lector de directorios que mantenga todo el estado, y almacenar esto indexado por la ruta en un diccionario. Cuando se solicita un nuevo directorio, verifique primero ese diccionario y solo cree un nuevo objeto si no hay ninguno para este directorio. De esta forma, los directorios parcialmente cargados se mantendrían hasta que se vuelvan a necesitar, y pueden continuar cargando en lugar de tener que empezar desde cero.

Pseudocódigo para el hilo:

while (forever) 
    new element = nil 
    if we have an active directory loader 
     tell directory loader to load one image 
     if false then make directory loader inactive 
     lock queue condition 
     if queue has elements 
      new element = retrieve LAST element (we aren't interested in the others) 
      empty queue 
      unlock with status "empty" 
     else 
      unlock queue 
    else 
     lock queue on condition "has elements" 
     new element = retrieve last element 
     empty queue 
     unlock with status "empty" 
    if new element != nil 
     if directory loader for new path does not exist 
      setup new directory loader for new path 
      store in dictionary 
      make it the "active" one 
     else 
      make the current one the "active" 

En cuanto al cargador de directorio, que podría ser algo como esto:

read one image: 
    if there are still images to read: 
     read, process and store one 
     return true 
    else 
     performSelectorOnMainThread with an "update GUI" method and the image list as parameter 
     return false; 

Esto es sólo un boceto rápido; hay algo de duplicación de código en el hilo, y la forma en que lo escribí solo actualizará la GUI después de que se hayan leído todas las imágenes, en lugar de hacerlas aparecer a medida que las leemos. Tendrá que copiar la lista de imágenes actual o agregar sincronización si quiere hacer eso.

Cuestiones relacionadas