Building apps all goes well in the beginning. You’re developing an app from scratch, it’s stable, and runs perfectly fine. After releasing the first version you get your first insights into occurring crashes and one of them is marked as an EXC_BAD_ACCESS error. This is where a journey starts to find out what is going on.
The first challenge is to reproduce the crash which can be really hard. Once you’re able to that, it’s still a challenge to find out the cause and the fix.
What does EXC_BAD_ACCESS mean?
EXC_BAD_ACCESS is an exception raised as a result of accessing bad memory. We’re constantly working with pointers to memory in Swift that link to a certain memory address. An application will crash whenever we try to access a pointer that is invalid or no longer exists. Such pointer is also known as a “dangling pointer”.
The root cause of such bad memory can differ. The referencing memory could be deleted or deallocated while the value of the pointer never gets updated. The pointer still points to the memory location of the deallocated memory and becomes a dangling pointer.
How to solve bad access exceptions
An EXC_BAD_ACCESS error in Swift is often causing frustration as it might not directly be clear what the cause of the crash is. However, if you know the possible causes it’s quite doable to quickly get to the root cause of the bad access exception.
If you don’t know how to reproduce the crash
A crash is pretty hard to fix if you don’t know how to reproduce it as you’re unable to verify that you’ve fixed the issue. You often have a certain idea of where the crash occurs but you’re unable to find the same crash in your app.
Make sure to run the exact same configuration of the crash. Install the right app version, on the same iOS and device if possible. This at least excludes that the crash is not reproducible due to mismatching configuration.
If you’re still unable to reproduce the crash, try out one of the following things:
- Look at the crash breadcrumbs, for example, in Firebase
- Contact one of your users that had the crash and ask for a Diagnostics report. Read more about it in my blog post 4 Tips to make it easier to fix crashes and bugs
- Watch the Understanding Crashes and Crash Logs session from WWDC 2018 and figure out the cause
Using the Address Sanitizer
Once you’re able to reproduce the crash locally it’s time to run a few checks to verify that you’re code is not containing any memory issues.
The Address Sanitizer, also known as ASan, finds memory corruptions and other memory errors at runtime. Apple even recommends using the ASan continuously during development:
Memory issues are frequently the cause of security vulnerabilities. You are strongly encouraged to incorporate the Address Sanitizer as part of your software qualification process.
The Address Sanitizer can be enabled in the scheme settings under diagnostics:
Once enabled, you start reproducing the crash again. The Address Sanitizer will pause the app whenever it detects a memory issue. See whether it finds any memory issues, solve it, and verify whether the EXC_BAD_ACCESS is still occurring.
Bad access due to race conditions
Data races could be another cause of bad memory access. Therefore, it’s worth to verify that your app is not containing any threading issues. You can use the Thread Sanitizer for this which is explained in detail in my blog post Thread Sanitizer explained: Data Races in Swift.
How about Zombie Objects?
Developers that have experience with Objective-C and bad access exceptions might think of Zombie objects to solve the memory issue. Zombie Objects can be enabled from the same diagnostics settings as we’ve used before to enable the Address Sanitizer. It keeps deallocated objects alive which can help to find the root cause of a memory issue if it’s related to accessing deallocated instances.
Swift compared to Objective-C does a lot more to prevent memory issues to occur. It’s more likely that you run into crashes like “Attempted to read an unowned reference” than into a bad access exception like you would in Objective-C for accessing deallocated instances. For those cases, you would enable Zombie Objects so you could still read the memory address of the deallocated memory that was accessed.
If you’re using unmanaged and unsafe pointers in Swift it’s more likely that you’ll run into memory-related crashes. Interfering with Objective-C frameworks could also cause these crashes to occur more often. If so, try to reproduce the crash again with Zombie Objects enabled and see whether it gives you more information to fix the crash.
Although the EXC_BAD_ACCESS is occurring a lot less in Swift than in Objective-C it’s still a common crash we run into. Hopefully, you’re able to solve those issues a lot easier now after reading this blog post.
If you like to improve your Swift knowledge, even more, check out the Swift category page. Feel free to contact me or tweet to me on Twitter if you have any additional tips or feedback.