kotterknife icon indicating copy to clipboard operation
kotterknife copied to clipboard

Reset cached bindings

Open MichaelRocks opened this issue 10 years ago • 6 comments

Fixes #5 by keeping a registry of cached lazies and resetting them on purpose.

MichaelRocks avatar Jan 08 '15 22:01 MichaelRocks

Seems this patch causes memory leaks so it shouldn't be merged. I'll try to find a better solution.

MichaelRocks avatar Jan 21 '15 10:01 MichaelRocks

The memory leak is fixed now. @JakeWharton, can you merge these commits please?

MichaelRocks avatar Feb 21 '15 19:02 MichaelRocks

Anybody alive here?

MichaelRocks avatar Mar 12 '15 15:03 MichaelRocks

without this change fragments cannot be used when the views are destroyed (e..g with page views )

clynamen avatar Mar 20 '15 20:03 clynamen

I support this. I would like to see this merged too.


@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> required(id: Int, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> t.finder(id) as V? ?: viewNotFound(id, desc) }

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> optional(id: Int, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> t.finder(id) as V? }

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> required(ids: IntArray, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> ids.map { t.finder(it) as V? ?: viewNotFound(it, desc) } }

@Suppress("UNCHECKED_CAST")
private fun <T : Any, V : View> optional(ids: IntArray, finder: T.(Int) -> View?)
        = Lazy { t: T, desc -> ids.map { t.finder(it) as V? }.filterNotNull() }

// Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it
private class Lazy<T : Any, V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> {
    private object EMPTY

    private var value: Any? = EMPTY

    override fun getValue(thisRef: T, property: KProperty<*>): V {
        if (value == EMPTY) {
            value = initializer(thisRef, property)
            LazyRegistry.register(thisRef, this)
        }
        @Suppress("UNCHECKED_CAST")
        return value as V
    }

    internal fun reset() {
        value = EMPTY
    }
}

private object LazyRegistry {
    private val lazyMap = WeakHashMap<Any, MutableCollection<Lazy<*, *>>>()

    fun register(target: Any, lazy: Lazy<*, *>) {
        lazyMap.getOrPut(target, { Collections.newSetFromMap(WeakHashMap()) }).add(lazy)
    }

    fun reset(target: Any) {
        lazyMap.get(target)?.forEach { it.reset() }
    }
}

object ButterKnife {
    fun reset(target: Any) {
        LazyRegistry.reset(target)
    }
}

mopsalarm avatar Nov 09 '15 18:11 mopsalarm

We need this functionality to correctly implement Fragments being detached and re-attached (ex: ViewPager).

tinsukE avatar Aug 08 '17 15:08 tinsukE