swiftdebuggingexc-bad-access

Finding the cause of EXC_BAD_ACCESS Code=1 on startup of Swift iOS App


I'm writing a Swift iOS app in XCode 12.5 that lets you take notes on "events" (interactions) with your contacts. After making some changes, launching the app on my phone yielded an EXC_BAD_ACCESS error from a part of the app I haven't touched in months - a notification manager that creates a reminder based on the latest notes you've taken on any of your contacts.

As background, I use CoreData to store the app's data on contacts and notes, using NSManagedObjects to represent Contact objects; each Contact object has 0 to many Event objects. The app was working fine on my iPhone as of a few months ago, and on the simulator as I've been making changes to the code in recent days/weeks. I've tried using the Instruments tool to check for Zombies and Leaks and have come up empty-handed. I've also tried to assess variables on the line of code causing the bad access error and can't find issues.

At a high level, this is the execution sequence leading to the problem:


        func getEvents(last90days: Bool = false) -> [Event] {
        var events = [Event]()
        let keyDate = Date(timeIntervalSinceNow: -90 * 60 * 60 * 24)

        for case let event as Event in (self.events ?? []) { // this is the line where the EXC_BAD_ACCESS occurs. Even if I nil-coalesce above this line separately, the problem still persists.
            if last90days && event.timestamp != nil {
                if event.timestamp == nil {
                    continue
                }
                if event.timestamp! < keyDate {
                    continue
                }
            }
            events.append(event)
        }
        
        return events
    }

_Error Message:_Thread 4: EXC_BAD_ACCESS (code=1, address=0x1fa9bf0) Stack Trace:

        * thread #4, queue = 'com.apple.usernotifications.UNUserNotificationServiceConnection.call-out', stop reason = EXC_BAD_ACCESS (code=1, address=0x1fa9bf0)
            frame #0: 0x00000001ace04334 libobjc.A.dylib`object_getMethodImplementation + 48
            frame #1: 0x00000001982d7c04 CoreFoundation`_NSIsNSSet + 40
            frame #2: 0x00000001981aaf18 CoreFoundation`-[NSMutableSet unionSet:] + 108
            frame #3: 0x000000019e3c93b0 CoreData`-[_NSFaultingMutableSet willReadWithContents:] + 636
            frame #4: 0x000000019e3e7ff4 CoreData`-[_NSFaultingMutableSet countByEnumeratingWithState:objects:count:] + 48
            frame #5: 0x000000019bd12bd0 libswiftFoundation.dylib`Foundation.NSFastEnumerationIterator.next() -> Swift.Optional<Any> + 180
          * frame #6: 0x0000000100dbb03c myApp`Contact.getEvents(last90days=false, self=0x0000000281b32f80) at Contact+helpers.swift:48:9
            frame #7: 0x0000000100db7bc8 myApp`InteractionAnalyzer.countInteractions(startDate=2022-01-03 04:12:17 UTC, endDate=2022-01-10 04:12:17 UTC, name=nil, onlyIncludeNewSparks=false, excludeNewSparks=false, sparkStartDate=nil, self=0x00000002838feb20) at InteractionAnalyzer.swift:24:48
            frame #8: 0x0000000100dfac18 myApp`NotificationManager.getNotificationString(self=0x000000028377a220) at NotificationManager.swift:74:60
            frame #9: 0x0000000100dfa368 myApp`NotificationManager.addNotificationRequest(self=0x000000028377a220) at NotificationManager.swift:62:29
            frame #10: 0x0000000100df9ab8 myApp`closure #1 in NotificationManager.addReminder(settings=0x0000000281355490, self=0x000000028377a220) at NotificationManager.swift:36:22
            frame #11: 0x0000000100df98d4 myApp`thunk for @escaping @callee_guaranteed (@guaranteed UNNotificationSettings) -> () at <compiler-generated>:0
            frame #12: 0x000000010146c0b4 libdispatch.dylib`_dispatch_call_block_and_release + 32
            frame #13: 0x000000010146dde0 libdispatch.dylib`_dispatch_client_callout + 20
            frame #14: 0x0000000101475ef0 libdispatch.dylib`_dispatch_lane_serial_drain + 788
            frame #15: 0x0000000101476d48 libdispatch.dylib`_dispatch_lane_invoke + 496
            frame #16: 0x0000000101483a50 libdispatch.dylib`_dispatch_workloop_worker_thread + 1600
            frame #17: 0x00000001e3f927a4 libsystem_pthread.dylib`_pthread_wqthread + 276

How can I get to the bottom of this cause? I've been crawling many threads on EXC_BAD_ACCESS to no avail and am hoping I'm just missing something dead simple..


Solution

  • The NSFaultingMutableSet in the stack trace pointed to an issue with accessing data from my Core Data store (e.g., Event objects like self.events). My notification manager was operating on a separate thread and creating an unstable situation where Core Data (which I hadn't set up properly for multi-thread access) was being read and modified on the main and secondary threads simultaneously.

    I was able to resolve the problem by wrapping the Notification Manager code that accesses Core Data objects in a DispatchQueue.main.async {...} block. There are other ways to set up Core Data objects for access from multiple threads (e.g., Coredata - Multithreading best way), but this was the simplest solution given multi-thread access isn't a priority for what I'm trying to do.