2011-07-07 9 views
18

¿Cómo puedo hacer que un bloque se ejecute sincrónicamente, o hacer que la función espere el controlador antes de la declaración de devolución, para que los datos puedan pasarse desde el bloque?Hacer que los bloques de iOS se ejecuten sincrónicamente

-(id)performRequest:(id)args 
{ 
__block NSData *data = nil; 

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) { 
     data = [NSData dataWithData:responseData]; 
    }]; 

    return data; 
} 
+0

editado para hacer un fragmento de código más específico – Sathya

+0

en cualquier punto del tiempo si se encuentra con la necesidad de sincronizar para sincronizar, hay algo terriblemente mal con su diseño. –

+0

Simplemente curioso, ¿por qué quieres hacerlo? Si solo desea completar el bloque en otro lugar, entonces su método "performRequest" también debe incluir un bloque al que se debe llamar dentro de la finalización del bloque interno. como Kunal señaló que hay algo mal con su diseño, ya que desea hacerlo. –

Respuesta

-1

Usted puede hacer petición síncrona en otro hilo, como a continuación código

-(void)performRequest:(id)args 
{ 

NSURLResponse *response = nil; 
NSError *error = nil; 
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; 
data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 
} 

De hilo principal se puede llamar a este

[self performSelectorInBackground:@selector(performRequest:) withObject:args]; 

o de lo que puede hacer solicitud asincrónica utilizando siguiente método

[NSURLConnection alloc]initWithRequest:request delegate:self]; 

e implementar métodos de delegado para NSURLConnection

+0

¿qué sucede si necesito acceder a una clase que no tiene otra alternativa que utilizar un bloque, por ejemplo: [classxyz requestAccessToAccountsWithType: accountType withCompletionHandler:^(BOOL concedido, error NSError *)}}; no tiene alternativas, corríjame si estoy equivocado – Sathya

+0

puede implementar métodos de delegado como dije en respuesta, el control vendrá en connectionDidFinishLoading: en caso de éxito de la finalización de la solicitud, si está solicitando desde el servidor – iMOBDEV

-1

¿Seguro que quieres hacerlo de forma síncrona? en caso afirmativo, puede llamar al (o poner) la función de controlador en su bloque o utilización del conocimiento de Jignesh (y utiliza “performSelectorInMainThread” cuando se acaba su manejador y desea devolver valores.

la manera asíncrona es (un poco poco) más difícil, pero mejor como:. - te obliga a escribir código limpio (sin paso de las variables convenientes) - se puede ejecutar otra cosa por lo que los usuarios no van a esperar y pensar que su aplicación es lenta

se realmente debería darle dos o tres horas para ir asincrónico. Pequeño dolor para una ganancia completa. También puede echarle un vistazo a Observación de valores-clave

-3

Puede hacer esto.

-(id)performRequest:(id)args 
{ 
__block NSData *data = nil; 

[xyzclass performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse   *urlResponse, NSError *error) { 

    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),^{ 

    data = [NSData dataWithData:responseData]; 

    }); 

}]; 

return data; 
} 
+0

me gustaría ver un buen simple tutorial para comprender esto porque, hasta ahora, los tutoriales son demasiado básicos e inútiles, como el ejemplo del multiplicador de pragmatic studio, o demasiado oscuros para ser útiles. ¿Sabes de alguno? – marciokoko

+3

¿No llega esto a la instrucción 'return' antes de que se llame al manejador? – Levi

+0

Levi es correcto – braden

2

Se podía añadir un método que procesa los datos devueltos y llamas que, en su bloque:

-(void)performRequest:(id)args{ 
    __block NSData *data = nil; 

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) { 
     data = [NSData dataWithData:responseData]; 
     [self processData:data]; //method does something with the data 
    }]; 
} 
3

asíncrono es casi siempre mejor. pero si quieres sincrónica:

-(id)performRequest:(id)args 
{ 
__block NSData *data = nil; 

    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) { 
     data = [NSData dataWithData:responseData]; 
    }]; 

    while(!data) { 
     [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 
    } 
    return data; 
} 

responsabilidad: CocoaCouchDeveloper dice que, por supuesto, esto sólo funcionará si el bloque de terminación y la runloop están en el mismo hilo. Supuse que porque muchos (la mayoría) de los manejadores de COMPLECIÓN sé que funcionan de esa manera, pero que son válidos en principio.

La anterior no es seguro para subprocesos uso
un semáforo o algo tal vez.
también dije que no promuevo esto

+0

descargo de responsabilidad: escrito en línea. [puede no ser el 100%, pero muestra claramente lo que debe hacer] –

+0

¿Puede asegurar que el acceso a la variable _data_ es seguro para subprocesos e involucra barreras de memoria? Dudo que lo sea. Si no hay barreras de memoria, el compilador puede guardar _data_ en un registro y, por lo tanto, _data_ nunca se actualiza en el hilo de llamada. – CouchDeveloper

+0

Bueno, en realidad, solo es confiable IFF el controlador de finalización se ejecuta en el mismo subproceso donde se ejecuta el ciclo de ejecución. Acabo de hacer una prueba simplificada, y resultó: la variable de datos se guardará en un registro que nunca se actualizará (y el ciclo while nunca volverá). Tampoco hay barreras de memoria, lo que significa que, si los datos se actualizan, no se garantiza que el acceso sea atómico/seguro, y el valor del puntero devuelto podría quedar paralizado. Pero: Objective-C evitará que el compilador use muchas oportunidades de optimización y las posibilidades aumentan de que funcione de todos modos;) – CouchDeveloper

21

Puede usar semáforos en este caso.

-(id)performRequest:(id)args 
{ 
    __block NSData *data = nil; 
    dispatch_semaphore_t sem = dispatch_semaphore_create(0); 
    [xyzclass requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) { 
     data = [NSData dataWithData:responseData]; 
     dispatch_semaphore_signal(sem); 
    }]; 
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); 
    return data; 
} 

semáforo bloqueará la ejecución de otras declaraciones hasta que se recibe la señal, esto va a asegurarse de que su función no devuelve prematuramente.

+0

Esta es la respuesta correcta, suponiendo que no está ejecutando en el hilo principal. –

Cuestiones relacionadas