objective c - tutorial - Conexión GCD y asíncrona
nsoperationqueue swift 3 example (4)
Sé que si creo un NSURLConnection (estándar async one), volverá a llamar al mismo hilo. Actualmente esto está en mi hilo principal. (trabajo bien también).
Pero ahora estoy usando el mismo código para otra cosa, y necesito mantener mi interfaz de usuario ágil ...
Si lo hago
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* and inside here, at some NSURLConnection is created */
});
... ¿es posible que mi NSURLConnection se cree pero mi hilo desaparece antes de que la conexión url haya regresado?
Soy nuevo en GCD. ¿Cómo se mantendría vivo el hilo hasta que se restableciera mi conexión url, o hay una mejor manera de hacerlo?
En primer lugar, su bloque y cada variable que use dentro se copiarán a GCD, por lo que el código no se ejecutará en su hilo sino en la cola global.
Si desea recuperar sus datos en el hilo principal, puede anidar una llamada asíncrona después de que se hayan recuperado sus datos:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"www..com"]];
NSURLResponse *response;
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
if (error) {
// handle error
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
// do something with the data
});
});
Pero, ¿por qué no utilizar el soporte asíncrono integrado de NSURLConnection? Necesitas un NSOperationQueue, pero si estás haciendo un montón de recuperaciones de red, es el camino a seguir de todos modos:
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"www..com"]];
[NSURLConnection sendAsynchronousRequest:request
queue:self.queue // created at class init
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
// do something with data or handle error
}];
Personalmente, utilizo una biblioteca como AFNetworking o ASIHTTPRequest para hacer que la red sea aún más fácil, que ambos soportan los bloques (el primero utiliza GCD y es un poco más moderno).
Entonces, realmente el problema no es el tiempo de vida del subproceso en el que se ejecuta el bloque, es el hecho de que este subproceso en particular no va a tener un runloop configurado y en ejecución para recibir ninguno de los eventos que regresan de la conexión.
Entonces, ¿cómo resuelves esto? Hay diferentes opciones para pensar. Puedo enumerar algunos, y estoy seguro de que otros enumerarán más.
1 - Podrías usar una conexión síncrona aquí. Una desventaja es que no obtendrá devoluciones de llamada para la autenticación, la redirección, el almacenamiento en caché, etc. (Todas las desventajas normales de las conexiones síncronas). Además, cada conexión bloqueará un subproceso durante un período de tiempo, así que si lo está haciendo muchos de estos podrían potencialmente tener algunos hilos bloqueados a la vez, lo que es caro.
2 - Si su conexión es simple y está usando iOS5, puede usar este método:
+ (void)sendAsynchronousRequest:(NSURLRequest *)request
queue:(NSOperationQueue*) queue
completionHandler:(void (^)(NSURLResponse*, NSData*, NSError*))
Esto iniciará una conexión asíncrona y luego le permitirá especificar un manejador de finalización (para el éxito o el fracaso) y una NSOperationQueue en la que desea programar el bloqueo.
De nuevo, tiene las desventajas de no obtener las devoluciones de llamada que pueda necesitar para la autenticación, el almacenamiento en caché, etc. Pero al menos no tiene hilos bloqueados por las conexiones que están en vuelo.
3 - Otra opción para iOS5 es configurar la cola para todas las devoluciones de llamada de delegado :
- (void)setDelegateQueue:(NSOperationQueue*) queue NS_AVAILABLE(10_7, 5_0);
Si usa esto, entonces todos los métodos de delegado se ejecutarán en el contexto de cualquier NSOperationQueue que especifique. Así que esto es similar a la opción # 2, espera que obtengas todos los métodos de delegado ahora para manejar la autenticación, la redirección, etc.
4 - Podría configurar su propio hilo que controle específicamente para administrar estas conexiones. Y al configurar ese hilo, configuras un runloop apropiadamente. Esto funcionaría bien en iOS4 y 5 y, obviamente, le ofrece todas las devoluciones de llamadas de delegado que desea manejar
5 - Podría pensar qué partes de su manejo asíncrono de conexión están realmente interfiriendo con su interfaz de usuario. Normalmente, el inicio de la conexión o la recepción de devoluciones de llamada de delegado no son tan caros. El costo costoso (o indeterminado) suele estar en el procesamiento de los datos que recopila al final. La pregunta que debe hacerse aquí es si realmente está ahorrando tiempo programando un bloque en alguna cola solo para iniciar una conexión asíncrona que se activará de inmediato y hará su trabajo en otro hilo de todos modos.
Por lo tanto, simplemente puede iniciar la conexión desde el hilo principal y recibir todas las devoluciones de llamada de delegado en el hilo principal, y luego, en su implementación de esos métodos de delegado, realice el trabajo costoso que necesita hacer en otra cola o hilo.
Así que algo como esto:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// go ahead and receive this message on the main thread
// but then turn around and fire off a block to do the real expensive work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Parse the data we''ve been collecting
});
}
De nuevo, esto no es exhaustivo. Hay muchas maneras de manejar esto, dependiendo de sus necesidades específicas aquí. Pero espero que estos pensamientos ayuden.
Solo como una respuesta a la razón por la cual su hilo estaba apareciendo (y para futuras referencias), NSURLConnection necesita un runloop. Si hubieras agregado
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantFuture]];
Vería que la conexión se ejecuta correctamente y que el hilo no desaparece hasta que la conexión se completó.
dispatch_queue_t queue = dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
[btnCreateSmartList setEnabled:NO];
[dbSingleton() createEditableCopyOfDatabaseIfNeeded];
[dbSingleton() insert_SMART_PlaceList:txtListName.text :0:txtTravelType.text: [strgDuration intValue]:strgTemprature:Strgender:bimgdt];
[self Save_items];
//*********navigate new
dispatch_async(dispatch_get_main_queue(), ^{
[activityIndicator stopAnimating];
[self performSelector:@selector(gonext_screen) withObject:nil afterDelay:0.0];
});
});