ios - tutorial - EXC_BAD_INSTRUCTION(código=EXC_I386_INVOP, subcódigo=0x0) en dispatch_semaphore_dispose
nsoperationqueue swift 3 example (8)
Obtengo EXC_BAD_INSTRUCTION (código = EXC_I386_INVOP, subcódigo = 0x0) en dispatch_semaphore_dispose, pero realmente no sé cómo rastrear la causa raíz de esto. Mi código hace uso de dispatch_async, dispatch_group_enter, etc.
ACTUALIZACIÓN: La causa del bloqueo se debe al hecho de que el servicio web Llamada (ver el código a continuación) nunca llama a Completar y cuando el código se ejecuta nuevamente, recibí el error EXC_BAD_INSTRUCCIÓN. Verifiqué que este es realmente el caso, pero no estoy seguro de por qué o cómo prevenir esto.
Código:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_group_t group = dispatch_group_create();
for (...) {
if (...) {
dispatch_group_enter(group);
dispatch_async(queue, ^{
[self webserviceCall:url onCompletion:^{
dispatch_group_leave(group);
}];
});
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)));
dispatch_sync(queue, ^{
// call completion handler passed in by caller
});
});
A veces, todo lo que se necesita para obtener un EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
es una declaración de return
faltante.
Ciertamente fue mi caso.
Aterrize aquí debido a una XCTestCase, en la que he desactivado la mayoría de las pruebas al ponerles un prefijo ''no_'' como en no_testBackgroundAdding. Una vez que noté que la mayoría de las respuestas tenían algo que ver con bloqueos y subprocesos, me di cuenta de que la prueba contenía algunas instancias de XCTestExpectation con la correspondiente waitForExpectations. Todos estaban en las pruebas de discapacidad, pero aparentemente Xcode todavía los estaba evaluando en algún nivel.
Al final encontré una XCTestExpectation que se definió como @property pero que carecía del @synthesize. Una vez que agregué la directiva de sintetizar, desapareció EXC_BAD_INSTRUCTION.
De su rastro de pila, EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
produjo porque dispatch_group_t
se liberó mientras aún estaba bloqueado (esperando por dispatch_group_leave
) .
Según lo que encontraste, esto fue lo que sucedió:
- Se creó el
dispatch_group_t group
. recuento de retención delgroup
= 1. -
-[self webservice:onCompletion:]
capturó elgroup
. recuento de retención delgroup
= 2. -
dispatch_async(...., ^{ dispatch_group_wait(group, ...) ... });
capturó elgroup
nuevo. recuento de retención delgroup
= 3. - Salga del alcance actual.
group
fue lanzado. recuento de retención delgroup
= 2. -
dispatch_group_leave
nunca fue llamado. -
dispatch_group_wait
fue tiempo de espera. El bloquedispatch_async
se completó.group
fue lanzado. recuento de retención delgroup
= 1. - Has vuelto a llamar a este método. Cuando
-[self webservice:onCompletion:]
se llamó de nuevo, el antiguo bloqueonCompletion
fue reemplazado por el nuevo. Entonces, el viejogroup
fue liberado.group
conteo de retención delgroup
= 0. Elgroup
fue desasignado. Eso resultó enEXC_BAD_INSTRUCTION
.
Para solucionarlo, le sugiero que averigüe por qué -[self webservice:onCompletion:]
no llamó al onCompletion
y lo onCompletion
. Luego, asegúrese de que la próxima llamada al método se realice después de que la llamada anterior haya finalizado.
En caso de que permita que se llame al método muchas veces, ya sea que las llamadas anteriores hayan finalizado o no, es posible que encuentre a alguien para mantener el group
para usted:
- Puede cambiar el tiempo de espera de 2 segundos a
DISPATCH_TIME_FOREVER
o una cantidad razonable de tiempo para que todos-[self webservice:onCompletion]
llamen a sus bloquesonCompletion
por el tiempo. De modo que el bloque endispatch_async(...)
lo mantendrá por usted.
O - Puede agregar
group
a una colección, comoNSMutableArray
.
Creo que es el mejor enfoque para crear una clase de dedicación para esta acción . Cuando desee realizar llamadas al servicio web , a continuación, cree un objeto de la clase, llame al método con el bloque de finalización que le liberará el objeto. En la clase, hay un ivar de dispatch_group_t
o dispatch_semaphore_t
.
En mi caso:
PHImageRequestOptions *requestOptions = [PHImageRequestOptions new];
requestOptions.synchronous = NO;
Estaba tratando de hacer esto con dispatch_group
Mi problema era que estaba creando objetos que quería guardar en un NSMutableDictionary pero nunca inicié el diccionario. Por lo tanto, los objetos fueron eliminados por la recolección de basura y se rompieron más tarde. Compruebe que tiene al menos una referencia fuerte a los objetos con los que está interactuando.
Mi problema era que estaba en mi init (). Probablemente el "yo débil" lo mató mientras que el init no estaba terminado. Lo moví del init y resolvió mi problema.
Mi problema fue tomar IBOutlet
pero no se conectó con el constructor de interfaz y el uso en un archivo rápido.
Tuve un problema diferente que me trajo a esta pregunta, que probablemente sea más común que el problema de sobre-publicación en la respuesta aceptada.
La causa raíz fue que nuestro bloque de compleción fue invocado dos veces debido a una falla de if / else en el manejador de red, lo que generó dos llamadas de dispatch_group_leave
por cada llamada a dispatch_group_enter
.
Bloque de finalización llamado varias veces:
dispatch_group_enter(group);
[self badMethodThatCallsMULTIPLECompletions:^(NSString *completion) {
// this block is called multiple times
// one `enter` but multiple `leave`
dispatch_group_leave(group);
}];
Depurar a través del count
de dispatch_group
En EXC_BAD_INSTRUCTION
, aún debe tener acceso a su dispatch_group en el depurador. Imprime el dispatch_group y verás:
<OS_dispatch_group: group[0x60800008bf40] = { xrefcnt = 0x2, refcnt = 0x1, port = 0x0, count = -1, waiters = 0 }>
Cuando vea count = -1
, indica que ya abandonó el grupo dispatch_group. Asegúrese de dispatch_enter
y dispatch_leave
al grupo en pares coincidentes.