Kotlin 中的 lambda 是否始终引用创建它的对象?

Does lambda in Kotlin always hold reference to the object where it was created?

提问人:pti4ik 提问时间:5/21/2023 更新时间:5/22/2023 访问量:201

问:

我有一个关于 kotlin lambda 和对 .让我们看一下这段代码:this

class MyViewModel: ViewModel {
    val eventFlow = mutableSharedFlow<Unit>()
}

class MyFragment: Fragment() {
    val viewModel = MyViewModel()

    val lambda_1: (Unit) -> Unit = { Log.v("TAG", "Event happened") }
    val lambda_2: (Unit) -> Unit = { processEvent() }

    fun processEvent() {
        //do smth
    }
    
    override fun onCreate(bundle: Bundle?) {
        lifecycleScope.launchWhenStarted {
            viewModel.eventFlow.collect(lambda_1)
        }
        lifecycleScope.launchWhenStarted {
            viewModel.eventFlow.collect(lambda_2)
        }
    }
}

请告诉我我是否正确:

  1. lambda_1不调用 的任何方法或变量,因此它不保留对对象的引用。MyFragmentMyFragment
  2. lambda_2从 调用方法,因此它确实保存对对象的引用。MyFragmentMyFrgament
  3. 如果 2nd 是正确的,那么当使用这样的 lambda 时,我们违反了 MVVM 的一点(“ViewModel 不得引用 View”),因为有一个引用链 -> -> -> 。viewModeleventFlowlambda_2MyFragment

试图读取字节码,但无法从中找出答案。

Android Kotlin Lambda 内存泄漏闭

评论


答:

5赞 broot 5/22/2023 #1

1. 是的,这是正确的。 不需要引用 ,所以它不保留一个。事实上,由于 可能是完全静态的,因此 Kotlin 编译器会创建一个单例,并为 的所有实例重用相同的实例。lambda_1MyFragmentlambda_1MyFragment

2.这也是正确的。 需要引用 ,所以它保留一个。在这种特定情况下,它的工作方式与内部类类似,因此当实例化 lambda 时,它会通过构造函数将引用传递给自身。一般来说,它是不同的,因为 lambda 包含对它需要的任何对象的引用,无论它是否是“外部”类。lambda_2MyFragmentMyFragment

3. 老实说,我对 MVVM 不是很熟悉,但我认为它的限制与一般的良好做法相同。

a). 如果你关注的是应用架构和关注点的分离,所以下层不应该调用上层,那么这种情况就不违反这个规则了。View 仅在 viewmodel 中注册事件,但 viewmodel 仍然不直接引用视图,也不知道它们。

如果你担心的是实例被泄露了,它不能被垃圾回收,那么在这种特定情况下这不是问题。 收集流量时使用。当片段被销毁时,它会取消所有启动的协程,流收集器也会被取消,因此不再需要 lambda,可以进行垃圾回收。MyFragmentMyFragmentlifecycleScopelifecycleScope