swift - 未捕获的 Kotlin 异常:kotlin.native.IncorrectDereferenceException:非法尝试访问非共享
问题描述
我尝试使用 Kotlin MPP(Multiplatform) 在 Android 和 iOS 之间开发共享库。但我面临关于iOS中线程的问题。对于我在 iOS 中的应用程序,我在主线程中建立了对象,但它可能会在另一个线程中调用该函数并抛出此异常,如下所示:
Uncaught Kotlin exception: kotlin.native.IncorrectDereferenceException: illegal attempt to access non-shared example.api.DrivingBehaviorDetector@397cba8 from other thread
at 0 DrivingBehaviorDetector 0x00000001037619d7 kfun:kotlin.Throwable.<init>(kotlin.String?)kotlin.Throwable + 87
at 1 DrivingBehaviorDetector 0x000000010375bca5 kfun:kotlin.Exception.<init>(kotlin.String?)kotlin.Exception + 85
at 2 DrivingBehaviorDetector 0x000000010375b9a5 kfun:kotlin.RuntimeException.<init>(kotlin.String?)kotlin.RuntimeException + 85
at 3 DrivingBehaviorDetector 0x0000000103781395 kfun:kotlin.native.IncorrectDereferenceException.<init>(kotlin.String)kotlin.native.IncorrectDereferenceException + 85
at 4 DrivingBehaviorDetector 0x0000000103782568 ThrowIllegalObjectSharingException + 744
at 5 DrivingBehaviorDetector 0x00000001037d72bc _ZNK27BackRefFromAssociatedObject19ensureRefAccessibleEv + 76
at 6 DrivingBehaviorDetector 0x00000001037c97c3 -[KotlinBase toKotlin:] + 35
at 7 DrivingBehaviorDetector 0x00000001037e3ef1 Kotlin_ObjCExport_refFromObjC + 65
at 8 DrivingBehaviorDetector 0x00000001037c4e37 objc2kotlin.125 + 167
at 9 Test 0x000000010340224d $sIeg_IeyB_TR + 45 (/Users/james/Documents/Projects/go/TestMPPforiOS/<compiler-generated>:<unknown>)
at 10 libdispatch.dylib 0x0000000103959dd4 _dispatch_call_block_and_release + 12
at 11 libdispatch.dylib 0x000000010395ad48 _dispatch_client_callout + 8
at 12 libdispatch.dylib 0x000000010396c460 _dispatch_root_queue_drain + 819
at 13 libdispatch.dylib 0x000000010396cb96 _dispatch_worker_thread2 + 132
at 14 libsystem_pthread.dylib 0x00007fff5245f6b3 _pthread_wqthread + 583
at 15 libsystem_pthread.dylib 0x00007fff5245f3fd start_wqthread + 13
(lldb)
我在这些文章中调查了解决方案:
Kotlin/Native中的不变性:Kotlin/Native 中的不变性
K/N 的协程和不变性:K/N 的协程和不变性。
我的 API(共享库)的入口如下:
class DrivingBehaviorDetector (
private var gravity:Vector?,
private var front:Vector?,
onGravityOrFrontChanged: ((newGravity: Vector?, newFront: Vector?, timestamp: Long) -> Unit)?,
onDrivingEventDetected: ((event: DrivingEvent) -> Unit)?
) {
private val lowPassFilter = LowPassFilter()
private val accProcessor = AccProcessor(gravity, front, onGravityOrFrontChanged, onDrivingEventDetected)
init {
gravity = gravity?.toCoreUnit()
front = front?.toCoreUnit()
}
fun addData(data:Acceleration) {
val rawAcc = data.toCoreUnit()
val filterAcc = lowPassFilter.lowPass(rawAcc)
accProcessor.addData(filterAcc)
}
fun addData(data:List<Acceleration>) {
for(acc in data) {
addData(acc)
}
}
}
我们可以注意到 API 非常简单,只需输入大量数据并回调一些计算的结果。但是在iOS中抛出异常和代码如下:
let drivingBehaviorDetector = DrivingBehaviorDetector(gravity: nil, front: nil, onGravityOrFrontChanged: { (newGravity, newFront, timestamp) in
print("newGravity = \(newGravity)")
print("newFront = \(newFront)")
}) { (event) in
print("event = \(event.description())")
}
let acc1 = Acceleration(vector: Vector(x: 0, y: 0, z: 0))
let acc2 = Acceleration(vector: Vector(x: 0, y: 100, z: 0))
drivingBehaviorDetector.addData(data: acc1)
DispatchQueue.global(qos: .background).async {
drivingBehaviorDetector.addData(data: acc2)
}
我能想象的唯一一个解决方案是强制在同一个线程中使用 API,但这似乎有点奇怪。是否可以通过修改共享库来解决这个问题。因为 mu 的使用很简单,只有一个“DrivingBehaviorDetector”,但它可以在不同的线程中调用“addData”函数。谢谢回答。
解决方案
对于 Kotlin/Native,必须明确定义正确的并发行为,以避免竞争。在这种特殊情况下,有几种选择:
drivingBehaviorDetector
冻结实例(例如通过调用构造freeze()
函数),并addData()
使用并发安全机制实现,例如冻结样本数据的链表或使用 Swift 集合实例- 考虑应用程序中的线程关联性,并决定谁是实例的所有者,
drivingBehaviorDetector
仅在由调度程序 API 强制执行的上下文/线程/队列中实例化和使用此实例 - 有专门的工作者/协程处理样本并将样本发送给该工作者,就像在https://github.com/JetBrains/kotlin-native/blob/8f7ebe7f511d5700cf559c854f9202bf55240e11/samples/videoplayer/src/videoPlayerMain/kotlin的视频样本播放器中所做的那样/DecoderWorker.kt#L384
推荐阅读
- python-3.x - 使用python将字节解码为字符串
- r - R:根据一列中的条件过滤行
- c - 带有标志“-”的 chdir() 函数;光盘 -
- python - Discord.py | outputs only bot in cycle
- python - Python or GTK Measure FPS (Frame Rate) of the Screen
- jenkins-pipeline - Equivalent Jenkins pipeline syntax for "Attach properties from files" field under PostBuild action in Jenkins
- javascript - 多日期范围比较javascript
- r - 在 R 中:使用 gtools 追加
- swiftui - 更新视图模型,发布对象,属性更新时
- java - Java中基于多个参数获取列表的子列表