As far as I understand, EXC_BAD_ACCESS happens when you try to access bad memory (feel free to correct me if I'm wrong)?
Is there a way to kind of catch it like in a try-catch
in Java
to prevent total app failure?
As far as I understand, EXC_BAD_ACCESS happens when you try to access bad memory (feel free to correct me if I'm wrong)?
Is there a way to kind of catch it like in a try-catch
in Java
to prevent total app failure?
Nope; EXC_BAD_ACCESS
means things have gone wildly off the rails. Your program is trying to access a memory address that is invalid. I.e. memory has been corrupted and there is no predictable recovery.
It may be a memory management issue. If you can reproduce the issue, turn on NSZombies and see what happens. Or post the backtrace here.
Note that try-catch style exceptions are non-recoverable in iOS/Cocoa, too. Exceptions are not to be used for recoverable error handling. That is what NSError is for.
A new C library SignalRecovery can enable programs to recover from operating system exceptions such as EXC_BAD_ACCESS
.
It can be used in IOS/MacOS/Linux
.
Sample code:
signal_try(label) {
// Add your code need try.
int* ptr = NULL;
*ptr = 0;
}
signal_catch(label) {
// Add your code to process exceptions, or do nothing.
siginfo_t* info = signal_info();
}
signal_end(label)
// Continue run
You can sometimes catch it in main
, with a signal handler. Doesn't allow you to do much, though, other than maybe log some stuff.
If you want catch or handle crashes with closure, you can use https://github.com/dhatbj/SignalRecovery
C code func macros are not available in swift and you cannot call functions directly (signal_try, signal_catch, signal_end) Here's an example of how to pass a closure as a function parameter.
First we need to pass completion block to C code and return signal error.
Add to signal_recovery.h this code:
const char* signalTry(void (*block)(void));
Add to signal_recovery.c this code:
const char* signalTry(void (*block)(void)) {
const char* signalName = "";
signal_try(label0) {
block();
}
signal_catch(label0) {
signalName = signal_name(signal_info()->si_signo);
}
signal_end(label0)
return signalName;
}
In swift code you need to call the C function signalTry and pass the closure to it as a parameter:
import // C files
class ExceptionsHandler {
private static var isReady = false
init() {
if !Self.isReady {
Self.isReady = true
signal_catch_init()
}
}
private func cFunction(_ block: @escaping @convention(block) () -> Void) -> (@convention(c) () -> Void) {
return unsafeBitCast(imp_implementationWithBlock(block), to: (@convention(c) () -> Void).self)
}
func execute(_ block: @escaping () -> Void) throws {
let cBlock: @convention(c) () -> Void = cFunction {
block()
}
let error = signalTry(cBlock)
if let error = error, !String(cString: UnsafePointer<CChar>(error)).isEmpty {
print("Catched signal \(String(cString: UnsafePointer<CChar>(error)))")
throw \\ error or exception
}
}
}
When an error EXC_BAD_ACCESS occurs inside closure block(), this C func will skip it and you will catch it without crash.
Like this:
try self.exceptionsHandler.execute {
let a = 3
let b = 0
let c = a / b
// or
let testArray: [Int] = []
let number = testArray[100]
}
A try catch can be used but you'll first need to know what caused the issue. You can enable NSZombie for your current build to catch the error and eliminate the need. Edit current scheme, enable NSZombie.