Tengo un punto de interrupción de todas las excepciones configurado en Xcode:
A veces, Xcode se detendrá en una línea como:
[managedObjectContext save:&error];
con el siguiente rastreo inverso:
pero el programa continúa como si nada hubiera pasado si hace clic en Continuar.
¿Cómo puedo ignorar estas excepciones "normales", pero aún así hacer que el depurador se detenga en las excepciones en mi propio código?
(Entiendo que esto sucede porque Core Data lanza y detecta excepciones internamente, y que Xcode simplemente está cumpliendo mi solicitud de pausar el programa cada vez que se lanza una excepción. Sin embargo, quiero ignorarlos para poder volver a depurar mi propio código !)
Moderadores: esto es similar al "filtrado de puntos de interrupción de excepción de Xcode 4" , pero creo que esa pregunta tarda demasiado en llegar al grano y no tiene respuestas útiles. ¿Pueden estar vinculados?
Respuestas:
Escribí un script lldb que te permite ignorar selectivamente las excepciones de Objective-C con una sintaxis mucho más simple, y maneja OS X, iOS Simulator y ARM de 32 bits y 64 bits.
Instalación
~/Library/lldb/ignore_specified_objc_exceptions.py
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')
Agregue lo siguiente a
~/.lldbinit
:command script import ~/Library/lldb/ignore_specified_objc_exceptions.py
reemplazándolo
~/Library/lldb/ignore_specified_objc_exceptions.py
con la ruta correcta si lo guardó en otro lugar.Uso
ignore_specified_objc_exceptions name:NSAccessibilityException className:NSSomeException
NSException
-name
coincideNSAccessibilityException
O-className
coincideNSSomeException
Debería verse algo como esto:
En su caso, usaría
ignore_specified_objc_exceptions className:_NSCoreData
Consulte http://chen.do/blog/2013/09/30/selectively-ignoring-objective-c-exceptions-in-xcode/ para obtener el script y más detalles.
fuente
Para las excepciones de Core Data, lo que normalmente hago es eliminar el punto de interrupción "Todas las excepciones" de Xcode y, en su lugar:
objc_exception_throw
(BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])
El punto de interrupción configurado debería verse así:
Esto ignorará cualquier excepción de datos básicos privados (según lo determinado por el nombre de clase con el prefijo
_NSCoreData
) que se utilizan para el flujo de control. Tenga en cuenta que el registro apropiado dependerá del dispositivo / simulador de destino en el que esté ejecutando. Consulte esta tabla como referencia.Tenga en cuenta que esta técnica se puede adaptar fácilmente a otros condicionales. La parte complicada fue crear los moldes BOOL y NSException para que lldb estuviera satisfecho con la condición.
fuente
$r0
:(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@”_NSCoreData”])
. Sin embargo, esto no funciona. Obtengo lo siguiente en la consola.Stopped due to an error evaluating condition of breakpoint 1.1: "(BOOL)(! (BOOL)[[(NSException *)$r0 className] hasPrefix:@”_NSCoreData”])" error: unexpected '@' in program error: 1 errors parsing expression
currentName
selector. Cambiarlo a[(NSException *)$eax name]
funcionó para mí.$r0
a$x0
(como se define aquí: sealiesoftware.com/blog/archive/2013/09/12/… ). La condición se convierte así en:(BOOL)(! (BOOL)[[(NSException *)$x0 className] hasPrefix:@"_NSCoreData"])
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:
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 depuración' y escriba 'punto de interrupción deshabilitar 2' (y establezca la casilla de verificación 'continuar automáticamente ... ).
Haga lo mismo para el punto de interrupción después de la línea ofensiva y haga que el comando 'breakpoint enable 2'.
La excepción de todos los puntos de interrupción ahora se activará y desactivará, por lo que solo estará activa cuando la necesite.
fuente