25

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?

corgichu
  • 2,580
  • 3
  • 32
  • 46

5 Answers5

30

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.

bbum
  • 162,346
  • 23
  • 271
  • 359
  • 1
    Re "try-catch style exceptions are non-recoverable in iOS" - that doesn't seem quite right to me. *certain* exceptions can't be caught by `@try / @catch`, but others can; I would consider the latter "recoverable", just like any other modern language with an exception-catching mechanism. – ToolmakerSteve Jan 31 '17 at 02:44
  • 1
    @ToolmakerSteve By definition, exceptions in iOS are not treated as recoverable. That is what NSError is far. What other modern languages do is entirely irrelevant. You can choose to do otherwise, but you are doing so counter to the documented, recommended, patterns and you will be chasing odd behavior and/or crasher bugs as a result. – bbum Jan 31 '17 at 20:56
  • 1
    Thanks. What does iOS actually *do* that would "treat an exception as not recoverable". Can you point me to a document or other thread that discusses how using exceptions would result in "chasing odd behavior or crasher bugs". [Have existing cross-platform code; want to make a more definitive statement about the reason to reorganize it all, not just "Apple says to, and everybody says "Oooo, better not use exceptions."] – ToolmakerSteve Feb 02 '17 at 22:08
  • @ToolmakerSteve When an exception is thrown that passes over any frame in the system frameworks, the behavior is undefined. It might leak, might not clean up, and/or might cause other undefined behavior. See https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/Exceptions/Exceptions.html#//apple_ref/doc/uid/10000012i See the **IMPORTANT** block at the top of the page. – bbum Feb 02 '17 at 23:16
  • Thanks. Indeed, I had read that warning, but I have not found any Apple docs or third-party threads that say **why** or **under what circumstances** there is a problem. You confirm what I suspected: the issue is that any call made into the system does not define what happens if an exception attempts to unwind past the call. Seems to me the only limitation that places on an app, is that any callback must catch exceptions at the latest within the callback's root method. Otherwise, I don't see how there would be any system frames involved in the stack unwind. – ToolmakerSteve Feb 03 '17 at 00:07
  • BTW, I don't disagree. Even on platforms that heavily use exceptions, without the framework risks of iOS, they are a dangerous tool that is falling out of favor. I just needed to quantify the extra risks on iOS. – ToolmakerSteve Feb 03 '17 at 02:28
  • @ToolmakerSteve If you want to use recoverable exceptions in your own code, you have to make sure that the boundary between your code and the system code is exactly defined. It is easy to leak exceptions; say, enumerate a collection and have an exception tossed during enumeration that is caught in the code that started the enumeration, for example. – bbum Feb 03 '17 at 20:42
6

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
Hasitha Jayawardana
  • 2,326
  • 4
  • 18
  • 36
Charles Wang
  • 61
  • 1
  • 1
  • Can you please help me on this a li'l more. How should I integrate this library in my XCode project and how can I use it. Getting EXC_BAD_ACCESS here: func addAck(_ ack: Int, callback: @escaping AckCallback) { acks.insert(SocketAck(ack: ack, callback: callback)) // on this line } – Peeyush karnwal Jun 25 '20 at 08:12
3

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.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
3

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]
        }
2

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.

  • Update * Swift2+ has excellent error handling now and is definitely worth checking out. Swift Error Handling
Mark McCorkle
  • 9,349
  • 2
  • 32
  • 42