我是 iOS 开发新手。我正在使用 Core Data 在我的 iOS 应用程序中保存数据。我的应用程序正在多个视图控制器中的应用程序委托访问共享对象,并通过该对象使用核心数据读取和保存数据。我还在那里使用 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 中,并且一次性会在视图控制器的
viewWillDisappear
函数中以及 App Delegate 中应用程序的 willResignActiveNotification
期间进行处置。它在视图控制器的 viewWillAppear
功能期间以及 App Delegate 中的应用程序 willEnterForegroundNotification
中恢复。
我通过 RxSwift 对所有 CPU 密集型操作(数据库读取、保存、网络请求等)使用异步调用。我还使用
context.perform
方法,与同步 performAndWait
相比,该方法是异步的。
这个bug并不是每次都会出现,而是时不时随机出现。应用程序运行正常,然后崩溃了。我观察到切换视图控制器或将应用程序置于后台然后置于前台可能会导致这些问题,但我在进入后台之前停止数据库操作,然后在进入前台后再次启动它们。
这是来自导致应用程序崩溃的线程的跟踪。
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 ()
我不知道出了什么问题。我读到 Core Data 不是线程安全的,所以这可能会导致错误,但我不知道如何在多线程 iOS 应用程序中使用它。您有什么建议吗?我该如何处理这个问题?或者也许您知道 Core Data 的任何线程安全替代方案吗?
我将不胜感激任何帮助或回答。
问候,
皮奥特尔
由于没有人回答我的问题,我将自己回答一下,以便为您遇到类似问题时提供可能的解决方案。
此错误可能是由 Apple 的 Core Data 库(iOS SDK 的一部分)的并发问题引起的。在我的项目中,我将大量数据同时保存到数据库中。我研究了这个主题,发现 Core Data 不是线程安全的,因此,由于这个事实,它可能不适合我的用例,并且此类并发错误可能会以非确定性方式不时发生。
我通过在项目中抽象持久层,然后通过 GRDB 库用 SQLite 数据库替换 Core Data 来解决这个问题。 GRDB 的作者声称,当我们使用适当的方法和事务时,该数据库是线程安全的,因此基本上切换到另一个持久层并使用适当的库来访问它,解决了我的问题。
编辑:
由于进一步的项目需求,我用 Apple iOS SDK 中提供的本机 sqlite3 实现替换了 GRDB 实现,并使用 RxSwift 数据类型(如
Observable
s 和 Completable
s)包装了所有数据库查询。之后,我可以使用适当的调度程序通过 subscribe
和 observe
方法将操作委托给特定线程。我还在 SQLITE_OPEN_FULLMUTEX
方法中添加了 sqlite3_open_v2
标志,以允许使用多个线程访问数据库。
总而言之,GRDB 和 sqlite3 + 适当的多线程实现都解决了这个问题。
在这里看到类似的 Piotr。运行时照片:
应用具体信息: LIBPLATFORM 客户端中的错误:os_unfair_lock 已损坏,或所有者线程在未解锁的情况下退出 中止原因 35334
Apple 开发者论坛出现了 SWIFT 之前的解决方案: 通过切换到 posix_spawn() 解决了该问题。不过我还没有在 10.6-10.11 上进行过测试。我的 GCD 版本无法在旧操作系统版本上运行。我的 NSTask 版本在 10.11 中开始失败,在 10.12 中根本无法工作。这就是为什么我一开始就改用 fork() 的原因。四分之一的 API 是可用的。
可能已经过时了?