问题描述
我一直在Kotlin跨平台上开枪,它很棒,但是线程让我感到困惑。线程之间的状态冻结从概念上讲是有意义的,并且在来回传递小对象或基元的简单示例中很好用,但是在现实世界的应用程序中,我无法解决InvalidMutabilityException。
class Mainviewmodel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)
private var coreroutinesupervisor = SupervisorJob()
private var coroutinescope: Coroutinescope = Coroutinescope(dispatchers.Main + coreroutinesupervisor)
private fun loadResults() {
// Here: Show loading
coroutinescope.launch {
try {
val result = withContext(dispatchers.Default) { objectWhichContainsNetworking.fetchData() }
// Here: Hide loading and show results
} catch (e: Exception) {
// Here: Hide loading and show error
}
}
}
没什么复杂的,但是如果在通用代码中使用并从Kotlin / Native运行,则在Mainviewmodel上注入InvalidMutabilityException。
这似乎是因为withContext中传递的所有内容都是递归冻结的,因此因为objectWhichContainsNetworking是Mainviewmodel的属性,并且在withContext中使用,然后Mainviewmodel陷入冻结状态。
所以我的问题是,这仅仅是对当前Kotlin /本机内存模型的限制吗?也许是协程的当前版本?还有什么办法可以解决这个问题?
注意:协程版本:1.3.9-native-mt。 kotlin版本1.4.0。
编辑1: 这样看来,上面精简的代码实际上可以正常工作。事实证明,导致该错误的代码是视图模型中的可更新var(用于保留对最后一个视图状态的引用),该变量被冻结,然后在尝试进行突变时引发异常。我将尝试使用“流/通道”以确保不需要var引用,并查看是否可以解决整个问题。
注意:如果有一种方法可以避免Mainviewmodel最初被冻结,那将仍然很棒!
编辑2: 将var替换为Flow。直到在这里使用辅助工具https://github.com/JetBrains/kotlinconf-app/blob/master/common/src/mobileMain/kotlin/org/jetbrains/kotlinconf/FlowUtils.kt,我才能在iOS中获得标准流程收集。
Mainviewmodel仍然被冻结,但是由于其所有状态都是不可变的,因此不再是问题。希望它能对某人有所帮助!
解决方法
在您的原始代码中,您引用的是父对象的字段,这将导致您捕获整个父对象并将其冻结。协程不是问题。协程遵循与Kotlin / Native中所有其他并发库相同的规则。跨线程时,它将冻结lambda。
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
)
//yada yada
private fun loadResults() {
coroutineScope.launch {
try {
val result = withContext(Dispatchers.Default) {
//The reference to objectWhichContainsNetworking is a field ref and captures the whole view model
objectWhichContainsNetworking.fetchData()
}
} catch (e: Exception) {}
}
}
为防止这种情况发生,
class MainViewModel(
private val objectWhichContainsNetworking: ObjectWhichContainsNetworking
){
init{
ensureNeverFrozen()
}
//Etc
内存模型最复杂的事情是这个。习惯于被捕获的内容并避免它。习惯了它并不难,但是您需要学习基础知识。
我已经详细讨论了这一点:
Practical Kotlin/Native Concurrency
Kotlin Native Concurrency Hands On
内存模型正在改变,但是要过一段时间才能实现。一旦适应了内存模型,就可以很容易地诊断出不可变的问题。