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

huangapple go评论97阅读模式
英文:

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

问题

  1. lambda_1 does NOT call any methods or variables of MyFragment, so it does NOT hold a reference to the MyFragment object.

  2. lambda_2 DOES call a method from MyFragment, so it DOES hold a reference to the MyFragment object.

  3. If 2nd is correct, then when using such a lambda, it can lead to a reference from viewModel to MyFragment, which could violate the principle in MVVM that "ViewModel must not have a reference to View." This potential reference chain is viewModel -> eventFlow -> lambda_2 -> MyFragment.

Please note that your understanding is accurate, and using such a lambda in this context might create an unintended reference between the ViewModel and the View, which should be avoided in MVVM architecture.

英文:

I have a question about kotlin lambdas and implicit references to this. Lets look at this code:

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)
        }
    }
}

Please tell me if I'm correct or not:

  1. lambda_1 does NOT call any methods or variables of MyFragment, so it does NOT hold reference to MyFragment object.
  2. lambda_2 DOES call a method from MyFragment, so it DOES hold a reference to MyFrgament object.
  3. If 2nd is correct, then when using such a lambda, we violate one of the points of MVVM ("ViewModel must not have reference to View"), because there is a reference chain viewModel -> eventFlow -> lambda_2 -> MyFragment.

Tried to read byte code but couldn't figure out answers from it.

答案1

得分: 4

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

  2. 这也是正确的。 lambda_2 需要引用 MyFragment,因此它保留了引用。在这种特定情况下,它的工作方式类似于内部类,因此当 MyFragment 实例化 lambda 时,它通过构造函数传递了对自身的引用。一般来说,lambda 会持有它需要的任何对象的引用,无论它是“外部”类还是其他类。

  3. 老实说,我对MVVM不太熟悉,但我假设它的限制与一般的良好实践相同。

a) 如果您担心的是应用程序架构和关注点分离,即较低层不应调用较高层,则这种情况不违反此规则。视图仅在视图模型中注册事件,但视图模型仍然不直接引用视图,也不了解它们。

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

英文:

1. Yes, this is correct. lambda_1 doesn't need a reference to MyFragment, so it doesn't keep one. As a matter of fact, because the lambda_1 could be entirely static, the Kotlin compiler creates a singleton and reuses the same instance for all instances of MyFragment.

2. This is also correct. lambda_2 needs a reference to MyFragment, so it keeps one. In this specific case it works in a similar way to an inner class, so when MyFragment instantiates the lambda, it passes a reference to itself through a constructor. In general it is different as lambda holds references to any object it needs, no matter if it is the "outer" class or not.

3. To be honest, I'm not very familiar with MVVM, but I assume its restrictions are the same as with general good practices.

a). If your concern is around the application architecture and separation of concerns, so lower layer should not call the higher layer, then this case doesn't violate this rule. View only registers an event in viewmodel, but viewmodel still doesn't reference views directly and doesn't know them.

b). If your concern is that the instance of MyFragment is leaked, it can't be garbage collected, then this is not a problem in this specific case. MyFragment uses lifecycleScope when collecting the flow. When the fragment is destroyed, its lifecycleScope cancels all launched coroutines, flow collector is also cancelled, and as a result the lambda is not needed anymore and can be garbage collected.

huangapple
  • 本文由 发表于 2023年5月21日 21:32:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76300168.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定