我是iOS开发的新手。我使用核心数据在我的iOS应用程序中保存数据。我的应用程序正在访问多视图控制器中的app的共享对象,并通过这个对象读取和保存带有Core数据的数据。我还在那里使用RxSwift,并做其他事情,比如向服务器发送数据。发送到服务器后,我会删除所有数据。
有时,我的应用程序崩溃了,我得到了以下错误:
线程名称:
Thread 14 Queue : com.apple.runningboardservices.background-workloop (serial)错误消息:
libsystem_platform.dylib`_os_unfair_lock_corruption_abort
"BUG IN CLIENT OF LIBPLATFORM: os_unfair_lock is corrupt, or owner thread exited without unlocking"它在以下函数中被调用:
func perform(_ function: @escaping (NSManagedObjectContext) -> Void) {
let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateContext.parent = context
privateContext.automaticallyMergesChangesFromParent = true
context.perform {
do {
function(privateContext)
guard self.context.hasChanges else { return }
try self.context.save()
self.context.reset()
} catch let error {
self.logger.error("Error while saving data \(error)")
}
}
}在context.perform行调试器上写着:Enqueued from rx.global_dispatch_queue.serial (Thread 2) Queue : rx.global_dispatch_queue.serial (serial)
这一职能的内容如下:
let bundle = Bundle(for: DatabaseFacade.self)
let modelUrl = bundle.url(forResource: modelName, withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: modelUrl)!
let persistentContainer = NSPersistentContainer(name: modelName, managedObjectModel: managedObjectModel)
persistentContainer.loadPersistentStores { (storeDescription, error) in
if let error = error as NSError? {
internalLogger.error("Unresolved error: \(error.localizedDescription), \(error.userInfo)")
}
}
context = persistentContainer.newBackgroundContext()此函数从另一个类中的另一个函数调用,如下所示:
func insertMany(events: [AcquisitionEvent]) {
if events.isEmpty { return }
database.perform { context in
do {
events.forEach { event in self.create(context: context, event: event as! SensorEvent) }
if context.hasChanges {
try context.save()
}
} catch let error {
self.logger.error("Error while saving entity: \(error)")
}
}
}在另一个类中调用insertMany函数,如下所示:
fileprivate func createMyDisposable() -> Disposable {
return myService
.observe()
.buffer(timeSpan: .seconds(1), count: 200, scheduler: Schedulers.serialBackground)
.subscribe(on: ConcurrentDispatchQueueScheduler.init(qos: .background))
.observe(on: SerialDispatchQueueScheduler.init(qos: .background))
.map(dao.insertMany)
.subscribe()
}将已创建的RxSwift一次性添加到DisposeBag中,在application中的视图控制器的viewWillDisappear函数和应用程序的willResignActiveNotification期间将一次性处理。它在视图控制器的viewWillAppear函数和application应用程序的willEnterForegroundNotification中恢复。
我在所有CPU密集型操作(数据库读取、保存、网络请求等)中使用异步调用。通过RxSwift。我还使用了context.perform方法,它是异步的,而不是同步的performAndWait。
这个错误并不是每次都会发生,而是时不时地随机发生。应用程序运行良好,然后就崩溃了。我观察到,切换视图控制器或将应用程序放到后台,然后放到前台可能会导致这些问题,但我在进入后台之前停止数据库操作,然后在前台之后再次启动它们。
下面是线程的跟踪,该线程使应用程序崩溃。
Thread 14 Queue : com.apple.runningboardservices.background-workloop (serial)
#0 0x00000001f2daf118 in _os_unfair_lock_corruption_abort ()
#1 0x00000001f2da9a20 in _os_unfair_lock_lock_slow ()
#2 0x000000019b1046c4 in objc_sync_enter ()
#3 0x000000018d952500 in -[RBSTarget shortDescription] ()
#4 0x000000018d9558cc in -[RBSAssertionDescriptor description] ()
#5 0x00000001842440f8 in _NS_os_log_callback ()
#6 0x000000019d2225d0 in _os_log_fmt_flatten_NSCF ()
#7 0x000000019d221d60 in _os_log_fmt_flatten_object ()
#8 0x000000019d21fb14 in _os_log_impl_flatten_and_send ()
#9 0x000000019d21db74 in _os_log ()
#10 0x000000019d223144 in _os_log_impl ()
#11 0x000000018d957ce0 in -[RBSConnection acquireAssertion:error:] ()
#12 0x000000018d961c90 in -[RBSAssertion acquireWithError:] ()
#13 0x000000019ccef05c in -[BKSAssertion acquire] ()
#14 0x000000019ccf0594 in -[BKSProcessAssertion acquire] ()
#15 0x00000001012006d4 in _dispatch_call_block_and_release ()
#16 0x00000001012023b4 in _dispatch_client_callout ()
#17 0x000000010120cb78 in _dispatch_workloop_invoke ()
#18 0x0000000101217e20 in _dispatch_workloop_worker_thread ()
#19 0x00000001f2db20f4 in _pthread_wqthread ()
Enqueued from com.apple.uikit.backgroundTaskAssertionQueue (Thread 12) Queue : com.apple.uikit.backgroundTaskAssertionQueue (serial)
#0 0x000000010120712c in dispatch_async ()
#1 0x000000018d942f20 in +[RBSWorkloop performBackgroundWork:] ()
#2 0x000000019ccf0f0c in -[BKSAssertion _acquireAsynchronously] ()
#3 0x000000019ccf0db0 in -[BKSProcessAssertion initWithBundleIdentifier:pid:flags:reason:name:withHandler:acquire:] ()
#4 0x000000019ccedc44 in -[BKSProcessAssertion initWithPID:flags:reason:name:withHandler:acquire:] ()
#5 0x00000001850c4efc in ___addBackgroundTask_block_invoke ()
#6 0x00000001012023b4 in _dispatch_client_callout ()
#7 0x00000001012138e4 in _dispatch_lane_barrier_sync_invoke_and_complete ()
#8 0x00000001850b9378 in _addBackgroundTask ()
#9 0x00000001850b5418 in -[UIApplication _beginBackgroundTaskWithName:expirationHandler:] ()
#10 0x000000018a0ab2e0 in +[NSPersistentStoreCoordinator _beginPowerAssertionNamed:withAssert:] ()
#11 0x000000018a067a90 in -[NSPersistentStoreCoordinator executeRequest:withContext:error:] ()
#12 0x000000018a039cd0 in -[NSManagedObjectContext save:] ()
#13 0x0000000102824d28 in closure #1 in DatabaseFacade.perform(_:) at /Users/pw/Development/wrk/dfp/ios/ios-sdk/iOS-SDK/Persistence/DatabaseFacade.swift:60
#14 0x0000000102824fe0 in thunk for @escaping @callee_guaranteed () -> () ()
#15 0x000000018a02b754 in developerSubmittedBlockToNSManagedObjectContextPerform ()
#16 0x00000001012023b4 in _dispatch_client_callout ()
#17 0x000000010120a540 in _dispatch_lane_serial_drain ()
#18 0x000000010120b290 in _dispatch_lane_invoke ()
#19 0x0000000101217e20 in _dispatch_workloop_worker_thread ()
#20 0x00000001f2db20f4 in _pthread_wqthread ()
#21 0x00000001f2db1e94 in start_wqthread ()
Enqueued from rx.global_dispatch_queue.serial (Thread 2) Queue : rx.global_dispatch_queue.serial (serial)
#0 0x0000000101206de0 in dispatch_async_f ()
#1 0x0000000102824b38 in DatabaseFacade.perform(_:) at /Users/pw/Development/wrk/dfp/ios/ios-sdk/iOS-SDK/Persistence/DatabaseFacade.swift:56
#2 0x00000001028d458c in SensorEventDao.insertMany(events:) at /Users/pw/Development/wrk/dfp/ios/ios-sdk/iOS-SDK/Persistence/Dao/SensorEventDao.swift:42
#3 0x00000001028d4e9c in protocol witness for Dao.insertMany(events:) in conformance SensorEventDao ()
#4 0x00000001028e5fc0 in implicit closure #2 in implicit closure #1 in UserDataAcquisition.createAccelerometerDisposable() at /Users/pw/Development/wrk/dfp/ios/ios-sdk/iOS-SDK/Acquisition/UserDataAcquisition.swift:112
#5 0x00000001028e5a4c in thunk for @escaping @callee_guaranteed (@guaranteed [AcquisitionEvent]) -> () ()
#6 0x00000001028e9d1c in partial apply for thunk for @escaping @callee_guaranteed (@guaranteed [AcquisitionEvent]) -> () ()
#7 0x00000001028e5a84 in thunk for @escaping @callee_guaranteed (@guaranteed [SensorEvent]) -> (@error @owned Error) ()
#8 0x00000001028e9d84 in partial apply for thunk for @escaping @callee_guaranteed (@guaranteed [SensorEvent]) -> (@error @owned Error) ()
#9 0x0000000101a92be0 in MapSink.on(_:) at /Users/pw/Development/wrk/dfp/ios/ifrodo/Pods/RxSwift/RxSwift/Observables/Map.swift:43
#10 0x0000000101a93414 in protocol witness for ObserverType.on(_:) in conformance MapSink<τ_0_0, τ_0_1> ()
#11 0x0000000101ab1664 in closure #1 in ObserveOnSerialDispatchQueueSink.init(scheduler:observer:cancel:) at /Users/pw/Development/wrk/dfp/ios/ifrodo/Pods/RxSwift/RxSwift/Observables/ObserveOn.swift:196
#12 0x0000000101ab1c68 in thunk for @escaping @callee_guaranteed (@guaranteed ObserveOnSerialDispatchQueueSink<τ_0_0>, @in_guaranteed Event<τ_0_0.ObserverType.Element>) -> (@out Disposable) ()
#13 0x0000000101a6fad8 in closure #1 in DispatchQueueConfiguration.schedule<τ_0_0>(_:action:) at /Users/pw/Development/wrk/dfp/ios/ifrodo/Pods/RxSwift/RxSwift/Schedulers/Internal/DispatchQueueConfiguration.swift:27
#14 0x0000000101a5d764 in thunk for @escaping @callee_guaranteed () -> () ()
#15 0x00000001012006d4 in _dispatch_call_block_and_release ()
#16 0x00000001012023b4 in _dispatch_client_callout ()
#17 0x000000010120a540 in _dispatch_lane_serial_drain ()
#18 0x000000010120b290 in _dispatch_lane_invoke ()
#19 0x0000000101217e20 in _dispatch_workloop_worker_thread ()
#20 0x00000001f2db20f4 in _pthread_wqthread ()
#21 0x00000001f2db1e94 in start_wqthread ()我不知道可能出了什么问题。我读到核心数据不是线程安全的,所以这可能会导致错误,但我不知道如何在多线程iOS应用程序中使用它。你有什么建议吗,我该如何处理这个问题?或者你知道核心数据的线程安全替代品吗?
我将感谢任何帮助或回答。
致以敬意,
皮奥特
发布于 2022-06-29 13:05:38
因为没有人回答我的问题,我会自己回答,如果你遇到类似的问题,我会给你提供可能的解决方案。
这个bug可能是由苹果核心数据库的并发问题引起的,它是iOS SDK的一部分。在我的项目中,我同时将大量数据保存到数据库中。我研究了这个主题,发现Core数据并不是线程安全的,因此由于这个事实,它可能不适合我的用例,并且这种并发错误可能会不时地以非确定性的方式出现。
我解决了这个问题,在我的项目中抽象持久层,然后通过SQLite库将核心数据替换为GRDB数据库。GRDB的作者声称,当我们使用适当的方法和事务时,这个数据库是线程安全的,所以基本上切换到另一个持久层并使用适当的库来访问它,解决了我的问题。
编辑
由于进一步的项目需求,我用Apple iOS SDK中可用的本机iOS实现替换了iOS实现,并用RxSwift数据类型(如Observables和Completables )包装了所有数据库查询。之后,我可以使用适当的调度器将操作委托给特定线程,使用subscribe和observe方法。我还将SQLITE_OPEN_FULLMUTEX标志添加到sqlite3_open_v2方法中,以允许使用多个线程访问数据库。
总之,GRDB和sqlite3 +适当的多线程实现都解决了这个问题。
https://stackoverflow.com/questions/72573424
复制相似问题