Espere a que la tarea asíncrona termine el bloque de finalización antes de volver al delegado de la aplicación

Estoy usando una subclass de UIManagedDocument para usar Core Data en mi proyecto. El punto es que la subclass devuelva una instancia singleton para que mis pantallas simplemente puedan llamarlo y el context del object administrado sigue siendo el mismo para todos ellos.

Antes de usar UIManagedDocument , tengo que prepararlo abriéndolo si ya existe su ruta de file, o si todavía no lo creo. prepareWithCompletionHandler: un método de prepareWithCompletionHandler: conveniencia prepareWithCompletionHandler: en la subclass para facilitar ambos escenarios.

 @implementation SPRManagedDocument // Singleton class method here. Then... - (void)prepareWithCompletionHandler:(void (^)(BOOL))completionHandler { __block BOOL successful; // _exists simply checks if the document exists at the given file path. if (self.exists) { [self openWithCompletionHandler:^(BOOL success) { successful = success; if (success) { if (self.documentState != UIDocumentStateNormal) { successful = NO; } } completionHandler(successful); }]; } else { [self saveToURL:self.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) { successful = success; if (success) { if (self.documentState != UIDocumentStateNormal) { successful = NO; } } completionHandler(successful); }]; } } @end 

Lo que trato de hacer es llamar a este método de preparación en el didFinishLaunchingWithOptions del delegado de la didFinishLaunchingWithOptions y esperar a que se ejecute el bloque de finalización ANTES de devolver YES o NO al final. Mi enfoque actual no funciona.

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { __block BOOL successful; SPRManagedDocument *document = [SPRManagedDocument shanetworkingDocument]; dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [document prepareWithCompletionHandler:^(BOOL success) { successful = success; }]; }); dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ }); return successful; } 

¿Cómo puedo esperar hasta que se prepareWithCompletionHandler manejador de finalización en prepareWithCompletionHandler antes de regresar con successful ? Estoy realmente confundido.

No estoy seguro de por qué el didFinishLaunching devolución de didFinishLaunching depende del éxito de su manejador de finalización, ya que aparentemente ni siquiera está considerando launchOptions . No me gustaría verte hacer una llamada síncrona (o más exactamente, usar un semáforo para convertir un método asíncrono en uno sincrónico) aquí, ya que ralentizará la aplicación y, si es lo suficientemente lento, corre el riesgo de ser asesinado por el process del perro de vigilancia.

Los semáforos son una técnica común para hacer un process asíncrono sincrónico:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { __block BOOL successful; SPRManagedDocument *document = [SPRManagedDocument shanetworkingDocument]; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [document prepareWithCompletionHandler:^(BOOL success) { successful = success; dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); return successful; } 

Pero, luego de una revisión adicional de qué está haciendo prepareWithCompletionHandler , aparentemente está invocando methods que envían sus propios bloques de finalización a la queue principal, por lo que cualquier bash de hacer esto sincrónico se interbloquee.

Entonces, usa patrones asynchronouss. Si desea iniciar esto en didFinishLaunchingWithOptions , puede hacer que se publique una notificación:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { __block BOOL successful; SPRManagedDocument *document = [SPRManagedDocument shanetworkingDocument]; [document prepareWithCompletionHandler:^(BOOL success) { successful = success; [[NSNotificationCenter defaultCenter] postNotificationName:kDocumentPrepanetworking object:nil]; }]; return successful; } 

Y luego puede tener su controller de vista addObserverForName para observar esta notificación.

Alternativamente, puede mover este código fuera del delegado de la aplicación y en ese controller de vista, eliminando la necesidad de la notificación.

Para su caso, usar el grupo de envío será ligeramente diferente:

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { __block BOOL successful; SPRManagedDocument *document = [SPRManagedDocument shanetworkingDocument]; dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [document prepareWithCompletionHandler:^(BOOL success) { successful = success; dispatch_group_leave(group); }]; }]; dispatch_group_wait(group, DISPATCH_TIME_FOREVER); return successful; } 

Una gran cantidad de soluciones propuestas aquí usando dispatch_group_wait o semáforos, pero la solución real es repensar por qué quiere bloquear el retorno de didFinishLaunching hasta después de que una request asíncrona posiblemente larga se complete. Si realmente no puede hacer otra cosa útil hasta que se complete la operación, mi recomendación sería mostrar algún tipo de carga, por favor espere pantalla mientras se realiza la initialization y luego regrese inmediatamente de didFinishLaunching.