2017-02-22 64 views
8

下面类有一个非常独特的生命周期,这需要我暂时空出lateinit性能如何设置lateinit科特林属性设置为null

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属性由匕首注入,我需要将它们设置为nullonDestroyView - 或有内存泄漏。据我所知,这在kotlin中是不可能的(没有反思)。我可以让这些属性可以为空,但这会破坏Kotlin无效安全的目的。

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

+0

为什么你有泄漏?也许问题出在SalesController而不是其属性上?我从来没有需要明确地设置为null注入由Dagger注入的属性,以避免泄漏问题... – Massimo

+0

@Massimo Conductor的控制器实例在配置更改后仍然存在https://github.com/bluelinelabs/Conductor – ZakTaccardi

+0

如果您需要取消它们,那么你就不需要'lateinit'。我敢肯定你没有任何泄漏,你只是混合了一些定义。如果您的演示者会引用您的视图,那么您会泄漏,反之亦然 – Dimezis

回答

7

科特林lateinit属性使用null作为一个未初始化的标志值,而且也没有干净的方式来设置nulllateinit属性的支持字段没有反射。


但是,Kotlin允许您使用委托属性覆盖属性行为。好像没有委托,它允许在kotlin-stdlib,但如果你需要的正是这种行为,你可以implement your own delegate要做到这一点,添加一些代码到你的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() 
    } 
} 
+0

我不认为您可以在'具有委托人的成员属性'上拥有'@Inject'。 – mfulton26

+0

@ mfulton26,感谢您的评论!你认为它可以用'@set:Inject'吗? – hotkey

+0

好想法。我认为它会(我不使用Dagger或任何东西,但至少编译)。 – mfulton26

0

我想你需要的是无lateinit的MADGIC正常健康nullable属性:

class SalesController : BaseController, SalesView { 
    @Inject @JvmField var viewBinder: SalesController.ViewBinder? = null 

有了这个解决方案,编译器会要求你检查viewBinder是否为null,但IMO在这里是合适的,因为它可以在程序的任何位置变成null

+0

这不起作用,因为某些属性在onCreateView/onDestroyView范围内应始终为非空值,而其他属性在该范围内实际上可以为空。如果一切都可以为空,那么我就无法区分这两者,我们又回到了Java的无效安全问题 – ZakTaccardi