2017-02-22 34 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 свойства впрыскивают кинжалом, и мне нужно, чтобы установить их null в onDestroyView - или есть утечка памяти. Это, однако, невозможно в котлине, насколько мне известно (без отражения). Я мог бы сделать эти свойства обнуляемыми, но это могло бы превзойти цель нулевой безопасности Котлина.

Я не совсем уверен, как это решить. В идеале может существовать какой-то тип обработчика аннотаций, который бы генерировал Java-код для автоматического исключения определенных переменных в onDestroyView?

+0

Почему у вас есть утечка? возможно, проблема заключается в SalesController, а не в его свойствах? Мне никогда не нужно явно указывать свойство, введенное кинжалом, чтобы избежать проблем с утечкой ... – Massimo

+0

@ Контролируемые экземпляры контроллера Massimo Conductor сохраняют изменения конфигурации https://github.com/bluelinelabs/Conductor – ZakTaccardi

+0

Если вам нужно их свести на нет, вам не нужно иметь «lateinit». И я уверен, что у вас там нет утечки, вы просто смешиваете некоторые определения. Вы бы просочились, если ваш ведущий ссылался бы на ваш вид, а не наоборот – Dimezis

ответ

7

Котлин lateinit свойство использует null как неинициализированное значение флага, и нет чистого способа установить null во вспомогательном поле lateinit собственности без отражения.


Однако Kotlin позволяет переопределить поведение свойств с помощью делегированных свойств. Кажется, нет ни один делегат, который допускает, что в kotlin-stdlib, но если вам нужно именно такое поведение, вы можете implement your own delegate сделать это, добавив код к вашим утилитами:

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' в свойстве member с делегатом. – mfulton26

+0

@ mfulton26, спасибо за замечание! Как вы думаете, он будет работать с '@set: Inject'? – hotkey

+0

Хорошее мышление. Я думаю, что это будет (я не использую Кинжал или что-нибудь, но, по крайней мере, это компиляция). – mfulton26

0

Я думаю, что вам нужно, это нормальная здоровая nullable собственность без madgic из lateinit:

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

С помощью этого решения компилятор попросит вас проверить, viewBinder is null или нет, но IMO здесь уместно, так как он может стать null в любой точке вашей программы.

+0

, это не сработает, потому что некоторые свойства всегда должны быть не нулевыми, если они включены в область' onCreateView'/'onDestroyView', тогда как другие свойства могут быть обнуляемы в пределах этой области. Если все обнуляется, тогда у меня нет возможности различать эти два вопроса, и мы снова возвращаемся к нулевой проблеме безопасности Java – ZakTaccardi