-
Notifications
You must be signed in to change notification settings - Fork 75
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change UIViewLazyList so cells can be removed and re-added without reuse #2242
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -314,22 +314,24 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> { | |
} | ||
|
||
private fun placeholderToLoaded( | ||
placeholder: Binding<V, W>?, | ||
binding: Binding<V, W>?, | ||
loadedContent: Widget<W>, | ||
): Binding<V, W> { | ||
// No binding for this index. Create one. | ||
if (placeholder == null) { | ||
if (binding == null) { | ||
return Binding(this).apply { | ||
setContentAndModifier(loadedContent) | ||
} | ||
} | ||
|
||
// We have a binding. Give it loaded content. | ||
require(placeholder.isPlaceholder) | ||
recyclePlaceholder(placeholder.content) | ||
placeholder.isPlaceholder = false | ||
placeholder.setContentAndModifier(loadedContent) | ||
return placeholder | ||
require(binding.isPlaceholder) | ||
if (binding.isBound) { | ||
recyclePlaceholder(binding.content!!) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a bound binding must have content, wdyt about restructuring the code to guarantee that requirement before this point? It looks like we could avoid some not-null assertions and have an access pattern here and below like...
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is a great idea. Let’s move this logic out of the code and into the type system! I’d like to do it in a follow-up though. |
||
} | ||
binding.isPlaceholder = false | ||
binding.setContentAndModifier(loadedContent) | ||
return binding | ||
} | ||
|
||
private fun loadedToPlaceholder(loaded: Binding<V, W>): Binding<V, W>? { | ||
|
@@ -438,8 +440,13 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> { | |
public var view: V? = null | ||
private set | ||
|
||
/** The content of this binding; either a loaded widget or a placeholder. */ | ||
internal lateinit var content: W | ||
/** | ||
* The content of this binding; either a loaded widget, a placeholder, or null. | ||
* | ||
* This may be null if its content is a placeholder that is not bound to a view. That way we | ||
* don't take placeholders from the pool until we need them. | ||
*/ | ||
internal var content: W? = null | ||
private set | ||
private var modifier: Modifier = Modifier | ||
|
||
|
@@ -458,9 +465,13 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> { | |
public val isBound: Boolean | ||
get() = view != null | ||
|
||
internal fun bind(view: V) { | ||
public fun bind(view: V) { | ||
require(this.view == null) { "already bound" } | ||
|
||
if (isPlaceholder && this.content == null) { | ||
this.content = processor.takePlaceholder() | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Without the structural change to a binding's state, should we add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Great idea. Lemme do that in the above-mentioned follow-up. |
||
this.view = view | ||
processor.setContent(view, content, modifier) | ||
} | ||
|
@@ -481,7 +492,8 @@ public abstract class LazyListUpdateProcessor<V : Any, W : Any> { | |
if (itemsAfterIndex != -1) processor.itemsAfter.set(itemsAfterIndex, null) | ||
|
||
// When a placeholder is reused, recycle its widget. | ||
processor.recyclePlaceholder(content) | ||
processor.recyclePlaceholder(content!!) | ||
this.content = null | ||
} | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So we can bind again later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a little dangerous to me--what are the odds that this causes a memory management issue, because it's being retained?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good instinct. I double checked this and
unbind()
breaks the cycle.But I don’t like that even with Redwood’s LeakTest, we don’t have dynamic coverage of leaks like this. I’ve opened #2245 to test it directly.