Ignore ciertas excepciones al usar el punto de corte All Exceptions de Xcode

Tengo un punto de interrupción de todas las excepciones configurado en Xcode:

captura de pantalla de un punto de interrupción de excepción configurado en el punto de corte de Xcode, configurado para emitir un sonido cuando se lanza una excepción

Algunas veces Xcode se detendrá en una línea como:

[managedObjectContext save:&error]; 

con la siguiente traza inversa:

traza inversa que muestra NSPersistentStoreCoordinator lanzando una excepción dentro de la llamada para guardar:

pero el progtwig continúa como si nada hubiera sucedido si hace clic en Continuar.

¿Cómo puedo ignorar estas excepciones “normales”, pero todavía tengo el depurador detenerse en excepciones en mi propio código?

(Entiendo que esto sucede porque Core Data internamente arroja y atrapa excepciones, y que Xcode simplemente está cumpliendo mi pedido de pausar el progtwig cada vez que se lanza una excepción. Sin embargo, quiero ignorar estos para poder volver a depurar mi propio código !

Moderadores: esto es similar al “filtrado de punto de corte de la excepción Xcode 4” , pero creo que esa pregunta lleva demasiado tiempo para llegar al punto y no tiene ninguna respuesta útil. Pueden estar vinculados?

Escribí un script lldb que le permite ignorar selectivamente las excepciones de Objective-C con una syntax mucho más simple, y maneja tanto OS X, iOS Simulator, y tanto ARM de 32 bits como de 64 bits.

Instalación

  1. Coloque esta secuencia de comandos en ~/Library/lldb/ignore_specified_objc_exceptions.py o en algún lugar útil.
 import lldb import re import shlex # This script allows Xcode to selectively ignore Obj-C exceptions # based on any selector on the NSException instance def getRegister(target): if target.triple.startswith('x86_64'): return "rdi" elif target.triple.startswith('i386'): return "eax" elif target.triple.startswith('arm64'): return "x0" else: return "r0" def callMethodOnException(frame, register, method): return frame.EvaluateExpression("(NSString *)[(NSException *)${0} {1}]".format(register, method)).GetObjectDescription() def filterException(debugger, user_input, result, unused): target = debugger.GetSelectedTarget() frame = target.GetProcess().GetSelectedThread().GetFrameAtIndex(0) if frame.symbol.name != 'objc_exception_throw': # We can't handle anything except objc_exception_throw return None filters = shlex.split(user_input) register = getRegister(target) for filter in filters: method, regexp_str = filter.split(":", 1) value = callMethodOnException(frame, register, method) if value is None: output = "Unable to grab exception from register {0} with method {1}; skipping...".format(register, method) result.PutCString(output) result.flush() continue regexp = re.compile(regexp_str) if regexp.match(value): output = "Skipping exception because exception's {0} ({1}) matches {2}".format(method, value, regexp_str) result.PutCString(output) result.flush() # If we tell the debugger to continue before this script finishes, # Xcode gets into a weird state where it won't refuse to quit LLDB, # so we set async so the script terminates and hands control back to Xcode debugger.SetAsync(True) debugger.HandleCommand("continue") return None return None def __lldb_init_module(debugger, unused): debugger.HandleCommand('command script add --function ignore_specified_objc_exceptions.filterException ignore_specified_objc_exceptions') 
  1. Agregue lo siguiente a ~/.lldbinit :

    command script import ~/Library/lldb/ignore_specified_objc_exceptions.py

    reemplazando ~/Library/lldb/ignore_specified_objc_exceptions.py con la ruta correcta si la guardó en otro lugar.

Uso

  • En Xcode, agregue un punto de interrupción para atrapar todas las excepciones de Objective-C
  • Edite el punto de interrupción y agregue un comando de depurador con el siguiente comando: ignore_specified_objc_exceptions name:NSAccessibilityException className:NSSomeException
  • Esto ignorará las excepciones donde NSException coincide con NSAccessibilityException O -className con NSSomeException

Debería verse algo como esto:

Captura de pantalla que muestra un punto de interrupción establecido en Xcode según las instrucciones

En tu caso, deberías usar ignore_specified_objc_exceptions className:_NSCoreData

Ver http://chen.do/blog/2013/09/30/selectively-ignoring-objective-c-exceptions-in-xcode/ para la secuencia de comandos y más detalles.

Para las excepciones de Datos centrales, lo que normalmente hago es eliminar el punto de interrupción “Todas las excepciones” de Xcode y en su lugar:

  1. Agregue un punto de objc_exception_throw simbólico en objc_exception_throw
  2. Establecer una condición en el punto de interrupción a (BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])

El punto de interrupción configurado debería verse más o menos así: Configurando el Punto de Inflexión

Esto ignorará cualquier excepción privada de Core Data (según lo determine el nombre de clase con el prefijo _NSCoreData ) que se usa para el flujo de control. Tenga en cuenta que el registro apropiado va a depender del dispositivo / simulador de destino en el que se está ejecutando. Eche un vistazo a esta tabla como referencia.

Tenga en cuenta que esta técnica se puede adaptar fácilmente a otros condicionales. La parte más complicada fue crear los moldes BOOL y NSException para que lldb estuviera satisfecho con la condición.

Aquí hay una respuesta rápida alternativa para cuando tiene un bloque de código, por ejemplo, una biblioteca de tercera parte que arroja múltiples excepciones que desea ignorar:

  1. Establezca dos puntos de interrupción, uno antes y un después de la excepción arrojando el bloque de código que desea ignorar.
  2. Ejecute el progtwig, hasta que se detenga en una excepción, y escriba ‘lista de puntos de interrupción’ en la consola del depurador, y encuentre el número del punto de ruptura ‘todas las excepciones’, debería verse así:

2: names = {‘objc_exception_throw’, ‘__cxa_throw’}, locations = 2 Opciones: disabled 2.1: where = libobjc.A.dylib objc_exception_throw, address = 0x00007fff8f8da6b3, unresolved, hit count = 0 2.2: where = libc++abi.dylib __cxa_throw, address = 0x00007fff8d19fab7, no resuelto, hit count = 0

  1. Esto significa que es el punto de interrupción 2. Ahora en xcode, edite el primer punto de interrupción (antes del código de lanzamiento de excepción) y cambie la acción a ‘comando de depurador’ y escriba ‘deshabilitar punto de interrupción 2’ (y establezca ‘continuar automáticamente …’ )

  2. Haga lo mismo para el punto de ruptura después de la línea ofensiva y tenga el comando ‘breakpoint enable 2’.

La excepción all breakpoints se activará y desactivará, por lo que solo estará activa cuando la necesite.