CoreData崩溃iOS 13+14:异常类型:EXC_BAD_ACCESS(SIGSEGV),异常子类型:KERN_



正在开发的应用程序在应用程序启动时间歇性地抛出上述异常。以下是触发崩溃的线程的堆栈跟踪。

0   libobjc.A.dylib                 0x00000001bbcaa148 object_getClass + 12 (objc-object.h:237)
1   CoreData                        0x00000001ae28e6b0 _PFObjectIDFastHash64 + 28 (NSBasicObjectID.m:706)
2   CoreFoundation                  0x00000001a84af10c __CFBasicHashRehash + 996 (CFBasicHash.c:477)
3   CoreFoundation                  0x00000001a84b2df4 CFBasicHashRemoveValue + 2352 (CFBasicHash.c:1386)
4   CoreFoundation                  0x00000001a83d12f8 CFDictionaryRemoveValue + 224 (CFDictionary.c:471)
5   CoreData                        0x00000001ae1de8e8 -[NSManagedObjectContext(_NSInternalAdditions) _forgetObject:propagateToObjectStore:removeFromRegistry:] + 120 (NSManagedObjectContext.m:5088)
6   CoreData                        0x00000001ae1be450 -[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 864 (NSManagedObjectContext.m:5077)
7   CoreData                        0x00000001ae2d6578 __90-[NSManagedObjectContext(_NSInternalNotificationHandling) _registerAsyncReferenceCallback]_block_invoke + 68 (NSManagedObjectContext.m:8825)
8   CoreData                        0x00000001ae2ccb18 developerSubmittedBlockToNSManagedObjectContextPerform + 156 (NSManagedObjectContext.m:3880)
9   libdispatch.dylib               0x00000001a80be280 _dispatch_client_callout + 16 (object.m:559)
10  libdispatch.dylib               0x00000001a809a4fc _dispatch_lane_serial_drain$VARIANT$armv81 + 568 (inline_internal.h:2548)
11  libdispatch.dylib               0x00000001a809afe8 _dispatch_lane_invoke$VARIANT$armv81 + 404 (queue.c:3862)
12  libdispatch.dylib               0x00000001a80a4808 _dispatch_workloop_worker_thread + 692 (queue.c:6590)
13  libsystem_pthread.dylib         0x00000001edcd85a4 _pthread_wqthread + 272 (pthread.c:2193)
14  libsystem_pthread.dylib         0x00000001edcdb874 start_wqthread + 8

在应用程序启动时,我使用一系列相互依赖的操作来从CoreData中提取和清理数据,以及通过异步调用服务器初始化的权限。我使用下面的CoreDataManager来处理CoreData的保存和获取。

正如我所说,这个问题似乎是随机发生的。如有任何帮助,我们将不胜感激。

/// A manager for interfacing with Core Data.
///
/// This class is a singleton, and the instance is accessible via the `shared` property.
class CoreDataManager {
/// The single shared instance of the `CoreDataManager` class.
static let shared = CoreDataManager()
/// The underlying `NSManagedObjectContext` that is being used by this manager.
let context: NSManagedObjectContext
private let container: NSPersistentContainer
private init() {
container = NSPersistentContainer(name: "FinSiteful")
container.loadPersistentStores(completionHandler: { (_, error) in
if let error = error as NSError? {
fatalError("Unresolved error (error), (error.userInfo)")
}
})
context = container.newBackgroundContext()
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
NotificationCenter.default.addObserver(self,
selector: #selector(contextDidSave(_:)),
name: Notification.Name.NSManagedObjectContextDidSave,
object: nil)
}
/// This function executes any time the managed object context is saved.
///
/// - Important: Other uses of the `notification` paramter
///
///     1. Use `notification.userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject>` to get inserted data
///     2. Use `notification.userInfo[NSDeletedObjectsKey] as? Set<NSManagedObject>` to get deleted data
///     3. Use `notification.userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject>` to get updated data
///
/// - Parameter notification: The `Notification` that contains the data updated, inserted, and/or deleted
@objc private func contextDidSave(_ notification: Notification) {
print("CoreDataManager: Context did save")
}
/// Creates a new managed object within the default context.
///
/// This method mostly exists for convenience. Below is an example of how to use this method to create
/// a new `Transaction` object which can be saved into CoreData.
/// ```
/// let trans: Transaction = CoreDataManager.shared.newObject()
/// ```
///
/// - Important: The context must be saved after calling this function in order for the object to be permanently
/// added to the database. Use the `saveContext()` method for this.
///
/// - Returns: A newly created `NSManagedObject`.
func newObject<T>() -> T where T: NSManagedObject {
T(context: self.context)
}
/// Executes the provided fetch request to retrieve data from Core Data.
///
/// - Parameter request: The `NSFetchRequest` to be executed.
/// - Returns: The results of `request`.
func fetch<T>(_ request: NSFetchRequest<T>) -> [T] where T: NSManagedObject {
do {
return try context.fetch(request)
} catch {
let nserror = error as NSError
fatalError("Unresolved error (nserror), (nserror.userInfo)")
}
}
/// Convenience wrapper around the context's delete method.
///
/// Specifies that the given object should be removed from its persistent store when changes are committed.
///
///  - Important: The context must be saved after calling this function in order for the changes to permanently take effect.
///  Use the `saveContext()` method for this.
///
/// - Parameter object: The object to be removed.
func delete(_ object: NSManagedObject) {
context.delete(object)
}
/// Saves any changes in the context to the database.
///
/// This method will only attempt to save if there are in fact changes that have been made.
///
/// Under normal circumstances, saving the context should never fail.
/// So, this method produces a `fatalError` in the event of a failure.
func saveContext() {
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error (nserror), (nserror.userInfo)")
}
}
}
}

更多信息。此错误特别发生在同时运行多个API请求的代码段中。

let group = DispatchGroup()
let dispatchSemaphore = DispatchSemaphore(value: 0)
let dispatchQueue = DispatchQueue(label: "transaction-downloads")
dispatchQueue.async {
self.dataCount = 500
while self.dataCount == self.MAXDATACOUNT {
group.enter()
self.saveTransactionsLocal(start: startDate, accessToken: accessToken, accountIDs: accountIDs) { response, error in
if response == .success {
if self.newEnd == self.end {
self.dataCount -= 1
}
self.end = self.newEnd
dispatchSemaphore.signal()
group.leave()
} else {
//Break out of loop
print("PH: saving transactions failed")
completion(.failure, error)
self.dataCount -= 1
}
}
dispatchSemaphore.wait()
}
completion(.success, nil)
}

长话短说,这段代码从API下载数据,将其保存到CoreData,如果一切顺利,它会再次这样做,直到从API接收到的对象数量小于500(您可以下载的最大值(。关键是,在操作过程中,该代码在for循环中被多次调用,所以在我的情况下,该代码对我下载数据的4个帐户运行了4次。

使用背景上下文时,应使用perform块。

例如保存时:

backgroundContext.performAndWait { 
guard backgroundContext.hasChanges else { return } 
try? backgroundContext.save()
}

提取:

backgroundContext.performAndWait { 
backgroundContext.fetch(request)
}

创建:

backgroundContext.performAndWait { 
CoreDataObject(context: backgroundContext)
}

你可以在这里阅读如何在后台使用核心数据,正如苹果公司所说:

由于队列是NSManagedObjectContext实例内部的私有队列,因此只能通过perform(:(和performAndWait(::(方法访问它

最新更新