18

下面的类有一个非常独特的生命周期,这需要我暂时清空lateinit属性

class SalesController : BaseController, SalesView {
    @Inject lateinit var viewBinder: SalesController.ViewBinder
    @Inject lateinit var renderer: SalesRenderer
    @Inject lateinit var presenter: SalesPresenter
    lateinit private var component: SalesScreenComponent

    override var state = SalesScreen.State.INITIAL  //only property that I want to survive config changes

    fun onCreateView(): View {  /** lateinit variables are set here */ }
    fun onDestroyView() {
         //lateinit variables need to be dereferences here, or we have a memory leak 
         renderer = null!! //here's the problem: throws exception bc it's a non-nullable property

} }

这是框架使用它的方式。

controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller
controller.onCreateView() //same instance of controller
controller.onDestroyView() //same instance of controller

我的lateinit属性是由 dagger 注入的,我需要将它们设置为nullin onDestroyView- 或者有内存泄漏。然而,据我所知(没有反思),这在 kotlin 中是不可能的。我可以使这些属性为空,但这会破坏 Kotlin 的空安全性的目的。

我不太确定如何解决这个问题。理想情况下,可能有某种类型的注释处理器会生成 java 代码以自动清除特定变量onDestroyView

4

1 回答 1

11

Kotlinlateinit属性null用作未初始化的标志值,并且没有明确的方法可以在没有反射的情况下在属性null的支持字段中进行设置。lateinit


但是,Kotlin 允许您使用委托属性覆盖属性行为。似乎没有允许 in 的委托kotlin-stdlib,但如果您确实需要这种行为,您可以实现自己的委托来做到这一点,向您的 utils 添加一些代码:

class ResettableManager {
    private val delegates = mutableListOf<ResettableNotNullDelegate<*, *>>()

    fun register(delegate: ResettableNotNullDelegate<*, *>) { delegates.add(delegate) }

    fun reset() { delegatesToReset.forEach { it.reset() } }
}

class Resettable<R, T : Any>(manager: ResettableManager) {
    init { manager.register(this) }

    private var value: T? = null

    operator fun getValue(thisRef: R, property: KProperty<*>): T =
            value ?: throw UninitializedPropertyAccessException()

    operator fun setValue(thisRef: R, property: KProperty<*>, t: T) { value = t }

    fun reset() { value = null }
}

以及用法:

class SalesController : BaseController, SalesView {
    val resettableManager = ResettableManager()
    @set:Inject var viewBinder: SalesController.ViewBinder by Resettable(resettableManager)
    @set:Inject var renderer: SalesRenderer by Resettable(resettableManager)
    @set:Inject var presenter: SalesPresenter by Resettable(resettableManager)

    fun onDestroyView() {
        resettableManager.reset()
    }
}
于 2017-02-22T17:46:52.880 回答