Skip to content
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

Merged
merged 2 commits into from
Aug 12, 2024

Conversation

swankjesse
Copy link
Collaborator

We observed in some test suites that table cells would observe a sequence of calls like this:

LazyListContainerCell.createView()
LazyListContainerCell.willMoveToSuperview(non-null) LazyListContainerCell.willMoveToSuperview(null)
LazyListContainerCell.willMoveToSuperview(non-null)

Note that we didn't receive a call to prepareForReuse() between the code being removed from its parent and added again. The code wasn't prepared for this sequence of calls.


  • CHANGELOG.md's "Unreleased" section has been updated, if applicable.

We observed in some test suites that table cells would observe a sequence
of calls like this:

LazyListContainerCell.createView()
LazyListContainerCell.willMoveToSuperview(non-null)
LazyListContainerCell.willMoveToSuperview(null)
LazyListContainerCell.willMoveToSuperview(non-null)

Note that we didn't receive a call to prepareForReuse() between
the code being removed from its parent and added again. The code
wasn't prepared for this sequence of calls.
}

// Unbind the cell when its view is detached from the table.
if (superview != null && newSuperview == null) {
binding?.unbind()
binding = null
Copy link
Collaborator Author

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

Copy link
Collaborator

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator

@hunterestrada hunterestrada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

return placeholder
require(binding.isPlaceholder)
if (binding.isBound) {
recyclePlaceholder(binding.content!!)
Copy link
Collaborator

Choose a reason for hiding this comment

The 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...

when (val state = binding.state) {
  is Bound -> { recyclePlaceholder(state.content) }
  is Unbound -> {}
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

if (isPlaceholder && this.content == null) {
this.content = processor.takePlaceholder()
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without the structural change to a binding's state, should we add require(this.content != null) for the non-placeholder case?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great idea. Lemme do that in the above-mentioned follow-up.

@swankjesse swankjesse merged commit d911e9a into trunk Aug 12, 2024
11 checks passed
@swankjesse swankjesse deleted the jwilson.0808.move_to_superview branch August 12, 2024 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants