2017-02-22 96 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