【问题标题】:How to provide an unfrozen or isolated swift implementation of a kotlin interface to Kotlin Native code如何为 Kotlin Native 代码提供解冻或隔离的 kotlin 接口的快速实现
【发布时间】:2021-08-03 11:51:03
【问题描述】:

我正在尝试创建一个多平台库,它需要一些特定于平台/应用程序的依赖项才能正常运行。作为一种解决方案,我想在 kotlin 代码中定义接口,并让宿主应用程序实现这些接口,并在使用库时将它们提供给库。

这个库将使用协程多线程在其他线程上做某些事情,它需要使用这些线程中的这些依赖项。

这是 kotlin native 的内存模型出现并咬我的地方,所以我找到了使用 Stately 的独立状态作为我的依赖项的解决方案,但这并没有按预期工作。从 iOS 单元测试中运行代码时,完全用 kotlin 代码编写,一切正常,但是当我尝试从实际的 iOS Swift 代码中调用它时,它变得很奇怪。

考虑下面的多平台示例代码,其中我为依赖创建者和依赖定义了接口,它们都需要在 swift 中实现。使用它,我可以将创建的依赖项放在 IsolateState 中,这样我就可以从多个线程访问它。

interface DependencyCreator {
    fun create(): Dependency
}

interface Dependency {
    fun foo()
}

class SomeClient(
    someDependencyCreator: DependencyCreator
) {
    init {
        println("is dependency creator frozen: ${someDependencyCreator.isFrozen}")
    }
    private val dependency: IsolateState<Dependency> = IsolateState {
        val theDependency = someDependencyCreator.create()
        println("is the dependency frozen? ${theDependency.isFrozen}")
        return@IsolateState theDependency
    }

    suspend fun doFoo() = withContext(Dispatchers.Default) {
        delay(100)
        dependency.access { it.foo() }
    }
}

我编写了以下单元测试代码,我使用 ios 目标 (./gradlew iosTest -i) 运行:

@Test
fun testFrozenDependency() {
    val dependencyCreator = object : DependencyCreator {
        override fun create(): Dependency {
            return object : Dependency {
                override fun foo() {
                    println("I foo'd")
                }
            }
        }
    }

    val client = SomeClient(dependencyCreator)
    runBlocking {
        client.doFoo()
    }
    assertTrue(true)
}

这个单元测试的结果是:

is dependency creator frozen: false
is the dependency frozen? false
I foo'd

但是,当我尝试在 iOS 上完全执行此操作时,它不起作用。这是我的 iOS 代码:

class iOSDependency : Dependency {
    func foo() {
        print("foo'd from ios")
    }
}

class iOSDependencyCreator : DependencyCreator {
    func create() -> Dependency {
        return iOSDependency()
    }
}

class ViewModel : ObservableObject {
    var client: SomeClient = SomeClient(someDependencyCreator: iOSDependencyCreator())
}

当我运行这段代码(ViewModel 被实例化)时,我得到以下结果:

is dependency creator frozen: true
is the dependency frozen? true
Function doesn't have or inherit @Throws annotation and thus exception isn't propagated from Kotlin to Objective-C/Swift as NSError.
It is considered unexpected and unhandled instead. Program will be terminated.
Uncaught Kotlin exception: kotlin.IllegalStateException: Mutable state shouldn't be frozen
    at 0   library                             0x0000000109adcf8f kfun:kotlin.Throwable#<init>(kotlin.String?){} + 95 (/Users/teamcity2/buildAgent/work/11ac87a349af04d5/runtime/src/main/kotlin/kotlin/Throwable.kt:23:37)
    at 1   library                             0x0000000109ad68cd kfun:kotlin.Exception#<init>(kotlin.String?){} + 93 (/Users/teamcity2/buildAgent/work/11ac87a349af04d5/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
    at 2   library                             0x0000000109ad6b3d kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 93 (/Users/teamcity2/buildAgent/work/11ac87a349af04d5/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
    at 3   library                             0x0000000109ad70ad kfun:kotlin.IllegalStateException#<init>(kotlin.String?){} + 93 (/Users/teamcity2/buildAgent/work/11ac87a349af04d5/runtime/src/main/kotlin/kotlin/Exceptions.kt:70:44)
    at 4   library                             0x0000000109b9276e kfun:co.touchlab.stately.isolate.StateHolder#<init>(1:0;co.touchlab.stately.isolate.StateRunner){} + 702 (/Users/runner/work/Stately/Stately/stately-isolate/src/nativeCommonMain/kotlin/co/touchlab/stately/isolate/Platform.kt:17:19)
    at 5   library                             0x0000000109b91694 kfun:co.touchlab.stately.isolate.createState$lambda-0#internal + 324 (/Users/runner/work/Stately/Stately/stately-isolate/src/commonMain/kotlin/co/touchlab/stately/isolate/IsoState.kt:49:30)
    at 6   library                             0x0000000109b918c3 kfun:co.touchlab.stately.isolate.$createState$lambda-0$FUNCTION_REFERENCE$2.invoke#internal + 163 (/Users/runner/work/Stately/Stately/stately-isolate/src/commonMain/kotlin/co/touchlab/stately/isolate/IsoState.kt:49:28)
    at 7   library                             0x0000000109b92162 kfun:co.touchlab.stately.isolate.BackgroundStateRunner.stateRun$lambda-1#internal + 354 (/Users/runner/work/Stately/Stately/stately-isolate/src/nativeCommonMain/kotlin/co/touchlab/stately/isolate/BackgroundStateRunner.kt:15:24)
    at 8   library                             0x0000000109b7f988 _ZN6Worker19processQueueElementEb + 3624
    at 9   library                             0x0000000109b7eb46 _ZN12_GLOBAL__N_113workerRoutineEPv + 54
    at 10  libsystem_pthread.dylib             0x00007fff61167109 _pthread_start + 148
    at 11  libsystem_pthread.dylib             0x00007fff61162b8b thread_start + 15

如您所见,与单元测试不同,提供的 swift 实现在“进入”多平台代码时被冻结。

目前我很茫然。如何让您的多平台库依赖于宿主应用程序提供的依赖项,并使用来自多个线程的这些依赖项(使用 IsolateState,因此应该解决所有冻结内存的问题)?是否有一些技巧可以不冻结 swift 生成的类实例?

我使用了以下版本:

  • kotlin 多平台 1.4.32
  • kotlinx-coroutines-core 版本 1.5.0-native-mt

【问题讨论】:

标签: swift kotlin kotlin-coroutines kotlin-multiplatform kotlin-native


【解决方案1】:

我还没有测试过任何东西,但是查看您的代码,似乎 Swift 类向 Kotlin/Native 运行时报告自己已冻结。如果是这种情况,那么作为一种解决方法,您需要将 Swift 类包装在 Kotlin 中的某些东西中:

data class DependencyWrapper(val dependency:Dependency)

class SomeClient(
    someDependencyCreator: DependencyCreator
) {
    private val dependency: IsolateState<Dependency> = IsolateState {
        DependencyWrapper(someDependencyCreator.create())
    }

    //etc
}

如果可行,请向 Stately 提出问题。检查可变值的冻结状态的目的是防止人们做明显错误的事情。它检查从someDependencyCreator.create() 返回的值以确保它是可变的。如果它被冻结,IsolateState 被认为是没有意义的。但是,如果 Swift 类将自己报告为已冻结,这很实用,如果不合逻辑的话,那么我认为 IsolateState 需要一种方法来覆盖默认行为。

(我写了IsolateState,作为上下文)

【讨论】:

  • 第一次尝试似乎有效,我将使用您的解决方案进一步调查,看看是否遇到其他问题。稍后会看看我是否可以在庄严的图书馆中提出问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 2019-10-03
  • 2020-10-17
  • 2018-06-25
  • 1970-01-01
  • 2019-12-24
  • 1970-01-01
相关资源
最近更新 更多