在 Swift iOS App 启动时查找 EXC_BAD_ACCESS Code=1 的原因

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

我正在 XCode 12.5 中编写一个 Swift iOS 应用程序,让您可以记录与联系人的“事件”(交互)。进行一些更改后,在我的 phone 上启动该应用程序时,应用程序的一部分出现了 EXC_BAD_ACCESS 错误,我已经几个月没有碰过它了——通知管理器会根据您最近的笔记创建提醒接管了您的任何联系人。

作为背景,我使用 CoreData 来存储应用程序的联系人和笔记数据,使用 NSManagedObjects 来表示联系人对象;每个 Contact 对象都有 0 到多个 Event 对象。几个月前,该应用程序在我的 iPhone 上运行良好,在模拟器上运行良好,因为我最近 days/weeks 一直在更改代码。我已经尝试使用 Instruments 工具来检查 Zombies 和 Leaks,但结果空手而归。我还尝试评估导致错误访问错误的代码行上的变量,但找不到问题。

概括地说,这是导致问题的执行顺序:


        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(代码=1,地址=0x1fa9bf0) 堆栈跟踪:

        * 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

我怎样才能查明这个原因?我已经在 EXC_BAD_ACCESS 上抓取了很多主题但都无济于事,我希望我只是遗漏了一些非常简单的东西..

将地址清理程序添加到您的方案中,并在模拟器上运行 应用程序。 Hopofully sanitizer 将更准确地向您显示代码中存在数据竞争的位置。

  1. 单击您的方案(在模拟器旁边)-> 编辑方案
  2. Select 诊断并检查“地址消毒器”
  3. 运行 您在模拟器上的应用程序

堆栈跟踪中的 NSFaultingMutableSet 指出了从我的核心数据存储访问数据的问题(例如,像 self.events 这样的事件对象)。我的通知管理器在单独的线程上运行并造成不稳定的情况,其中核心数据(我没有为多线程访问正确设置)在主线程和辅助线程上同时读取和修改。

我能够通过将访问核心数据对象的通知管理器代码包装在 DispatchQueue.main.async {...} 块中来解决问题。还有其他方法可以设置 Core Data 对象以从多个线程访问(例如,),但这是最简单的解决方案,因为多线程访问不是我正在尝试做的优先事项。