英文:
Does lambda in Kotlin always hold reference to the object where it was created?
问题
-
lambda_1
does NOT call any methods or variables ofMyFragment
, so it does NOT hold a reference to theMyFragment
object. -
lambda_2
DOES call a method fromMyFragment
, so it DOES hold a reference to theMyFragment
object. -
If 2nd is correct, then when using such a lambda, it can lead to a reference from
viewModel
toMyFragment
, which could violate the principle in MVVM that "ViewModel must not have a reference to View." This potential reference chain isviewModel
->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:
lambda_1
does NOT call any methods or variables ofMyFragment
, so it does NOT hold reference toMyFragment
object.lambda_2
DOES call a method fromMyFragment
, so it DOES hold a reference toMyFrgament
object.- 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
-
是的,这是正确的。
lambda_1
不需要引用MyFragment
,因此它不保留引用。事实上,由于lambda_1
可能完全是静态的,Kotlin 编译器创建了一个单例,并为所有的MyFragment
实例重用相同的实例。 -
这也是正确的。
lambda_2
需要引用MyFragment
,因此它保留了引用。在这种特定情况下,它的工作方式类似于内部类,因此当MyFragment
实例化 lambda 时,它通过构造函数传递了对自身的引用。一般来说,lambda 会持有它需要的任何对象的引用,无论它是“外部”类还是其他类。 -
老实说,我对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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论