6

He visto algunas de las presentaciones de WWDC 2010 y también he leído la mayoría de los documentos sobre bloques y simultaneidad y tengo algunas preguntas sobre el uso de bloques con colas en serie en Grand Central Dispatch. Tengo un proyecto de iOS 4 que tiene una vista de desplazamiento y un diccionario de información de imagen: direcciones URL de las imágenes, etc. Quiero usar GCD y bloques para descargar las imágenes y ponerlas en mi scrollview, sin bloquear el hilo principal. He writen el siguiente código que parece funcionar:iOS 4 GCD questions

for (NSDictionary* dict in images) 
{ 
    dispatch_async(image_queue, ^{ 

      NSString* urlString = [dict objectForKey:@"url"]; 
      NSURL* url = [NSURL URLWithString:urlString]; 
      NSData* imageData = [[NSData alloc] initWithContentsOfURL:url]; 
      UIImage* image = [UIImage imageWithData:imageData]; 
      UIImageView* imageView = // initialize imageView with image;  

      dispatch_async(dispatch_get_main_queue(), ^{ 
       [self.scrollView addSubview:imageView]; 
      }); 
      [imageData release]; 
     }); 
} 

Tengo dos preguntas:

  1. De acuerdo con la guía de concurrencia que no debería capturar variables del ámbito de inclusión que son tipos no escalares - en mi código, capturo el dict que es un objeto NSDictionary *. Si no puedo capturarlo, ¿cómo debo escribir el código? ¿Un bloque solo captura variables del ámbito adjunto que se utilizan realmente?

  2. ¿Qué sucede si dejo ViewController actual antes de que todas las imágenes se obtengan a través de la cola de envío en serie? No creo que sean conscientes de que el ViewController que los creó se ha ido, así que ¿qué sucede cuando ejecutan el controlador de finalización donde inserto las vistas de imagen en mi scrollview en el hilo principal? ¿Causa un error o qué? ¿Y cómo puedo cancelar cualquier operación restante en la cola serie cuando desaparece mi ViewController?

Saludos,

+0

Me gustaría ver algunas de las mejores prácticas en esto también. –

Respuesta

10
  1. Si bien es un punto insignificantes, esto es importante para la comprensión de lo que la guía de concurrencia está tratando de decirle: Punteros son tipos escalares. De modo que puede capturar punteros dentro de bloques todo lo que quiera ... ¡PERO debe ser consciente de la duración de la memoria que señalan! NSDictionary * es una especie de id. Y cuando hace referencia a un id en un bloque, el tiempo de ejecución asume la responsabilidad de conservar el id si el bloque se copia (que es por dispatch_async()) y luego lo libera cuando el bloque en sí mismo está desasignado. Y sí, un bloque solo captura las variables a las que se hace referencia dentro de él.

  2. Como ahora sabe que el bloque asíncrono ha realizado un retenido automático, debe quedar claro (er) que (errores de gestión de la memoria modulo) su ViewController no puede "desaparecer" hasta que el bloque esté listo. Por lo tanto, no va a fallar, pero tiene razón al notar que realmente desea una forma de cancelar este tipo de trabajo asincrónico cuando en realidad no está planeando utilizar los resultados. Un patrón simple pero efectivo es poner una prueba al principio de su bloque asíncrono que verifica si el trabajo aún debe realizarse.

1

@Kaelin Colclasure: para la primera pregunta parece más probable un problema de estado compartido en aplicación multiproceso: de tipo integral que tiene una copia de valor (que se aplica a los punteros también), pero cuando se va a utilizar una Al objeto referenciado por un puntero, tendrá todos los problemas relacionados con la ausencia de bloqueo (tipo de variación en el problema de la duración del objeto que mencionó aquí).

0

Aquí está la diferencia práctica para su primer punto:

Si paso un escalar en un bloque utilizado en una cola GCD, a continuación, en el bloque Estoy trabajando con una copia de los datos originales. Los cambios no serán visibles fuera del bloque. (Hay advertencias sobre esto, el modificador __block, por ejemplo, pero en general esto es correcto.)

Si paso, digamos, un NSMutableDictionary en un bloque, los cambios en el diccionario se ser visible fuera del bloque - esto se debe a que retuvo una referenciaal diccionario y no tomó una copia profunda .

En cualquier caso, la administración de la memoria se realiza por usted, ya sea que se copie la variable escalar o que se retengan los objetos.

Dado que no puede cambiar los contenidos de NSDictionary después de que se haya inicializado, probablemente encontrará que los bloques hacen automáticamente Lo correcto para usted.

Para el segundo punto, la administración de la memoria es prácticamente automática a menos que necesite trabajar en una copia de un objeto mutable.

3

Como otros respondieron sus dos preguntas, me gustaría comentar su código y recomendarle que no use GCD para consultas de red como imágenes. El problema principal con ellos es que todos se ejecutarán al mismo tiempo. Dependiendo de la cantidad de descargas, puede crear demasiadas conexiones simultáneas que pueden empantanar una conexión celular, y el usuario puede terminar pensando en algo incorrecto si las imágenes no comienzan a aparecer mientras luchan por la preciosa red .

Intente utilizar un NSOperationQueue con un valor maxConcurrentOperationCount de 2 o 3. Esto le permitirá poner en cola una cantidad potencialmente infinita de solicitudes de red, pero tener como mucho algunas funciones en paralelo. Como puede acceder al estado de la red del dispositivo, puede aumentarlo de manera condicional para decir 8 para conexiones wifi.

Y el segundo problema con GCD es que es un poco engorroso cancelar operaciones pendientes. Si su usuario ingresa a un controlador de vista y luego lo empuja hacia atrás, dependiendo de cómo haya programado su código, los bloques GCD retendrán el controlador de vista, evitarán que se libere y, de hecho, todas las operaciones de red deberán finalizar hasta que el controlador pueda liberado (así que no tiene sentido cancelar las conexiones en dealloc).

Lo aprendí de la manera más difícil monitoreando un proxy y yendo rápidamente dentro de una vista y viceversa. Fue realmente espantoso ver cómo las conexiones pendientes infligieron aproximadamente 20 segundos de daño de ancho de banda de red adicional a en mi aplicación, que de otra manera no sería consciente.

tl; dr utiliza una cola para la red, GCD para el procesamiento de imágenes/escalado/actualización de la GUI.

+0

Los bloques GCD solo se ejecutarán simultáneamente si se envían en una cola de despacho global. Si crea una cola de despacho en serie (una cola de despacho privada), las tareas se ejecutarán secuencialmente. Consulte [Guía de programación de concurrencia de Apple] (http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html). –

+0

Claro, el problema es que no sabes qué es 'image_queue', podría haber sido asignado a' dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_LOW, 0) '. –

+0

@Ducan parece que el problema con el uso de una cola en serie es que las cosas son totalmente secuenciales en lugar de tener la concurrencia restringida que permite maxConcurrentOperationCount como dijo Grzegorz. – Lee