-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Out of bounds collectionview update (crash) #503
Comments
@heumn thanks for reporting and including an example project! I'm going to spin through this and see what's going on. |
Thanks. Really interested in what you will find. The crash broke my brain 😅 |
This will happen.
Since
Following might help:
It's recommend to make identifier unique. |
Every time But when And in
|
I think I understand what you mean and if I understood you correctly what I want to do is not possible (sadface) But then my understand is that this PR #494 would enable what I want? I apologize as English is not my first language I might be misunderstanding what you say. Again, thank you so much for helping out 🙌 Even this will crash (make sure that the items are also equal, a missing thing in original zip):
|
Because
Make the two methods above consistent.
By the way, I'm not native English speaker😄 |
This confuses me. I though one was for identifying unique objects (as in ID) but the other one was for identifying changes in a unique object. Having them return equality for the same object that has changes to it would not only mean you would never get "reload" on sections, it also doesn't make sense to have two methods mean the same thing. |
@heumn ack, so everything is correct, but the batch update is using the wrong That's because when the update is queued inside Here's some proof of the issue: I'm still rooting to fully understand the chain of events, but the biggest issue is the Will try to come up w/ an elegant solution that doesn't require a bit update, might require updating like #494... Also lights a 🔥 that we need to get a stock embedded section controller built to help avoid these situations! Hang in there @heumn, we'll get this! cc @PhilCai1993 too in case you have any ideas. |
Thanks a lot for the update! If not tomorrow then over the weekend I will spend some time digging a bit deeper to see if I can find a nice way to solve this :) |
@heumn there's a good chance this explains some low-firing crashes internally too! Really excited about this find. |
Ok, looking more into this was more fun than doing other stuff. To start off, let me repeat again how awesome this library is! I immediately dropped my internal While using the framework, I ran into the following code inside a func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext!.dequeueReusableCell(of: EmbeddedCollectionViewCell.self, for: self, at: index) as! EmbeddedCollectionViewCell
adapter.collectionView = cell.collectionView
return cell
} I know this Especially when I saw this : We are as the user of the API are essentially giving away any control of the When I built my custom controller for a similar type of behaviour I did it the other way around, meaning the adapter didn't really know if the UICollectionView would still be "his" / available, but rather know "ok I need to update, where is the Random ideas that comes to mind to solve this
Quickfix:
Apologies for the long comment |
@heumn awesome that you found a quick fix! I'll try to address all of your points.
The code you outlined (all the work in the setter) was added specifically to handle cell reuse w/ embedded collection views. This setup is how we use it in Instagram 😄
That is very very much by design, since the point of the adapter is to handle edge cases, updating, and provide APIs, all of which require the collection view to work in a specific way. Agreed that there is a bit of magic, but being open source, I wouldn't go as far as saying "black box" 😅.
This was a #greatdebate when building IGListKit. Reusing SCs makes tracking state (e.g. "selected") trickier b/c you have to map sections/indices/cells to state vs just a
Once we provide a setup to do embedded lists, this will definitely be addressed (custom SC and cell).
We actually don't update and fallback to reload when there is no window, but I think cells that are in the reuse pool are still subviews but have
We're planning on getting rid of Can you expand on what visibility means here? Not sure I follow. |
Hehe.. I understood that, but arent you still keeping the state/knowedge about the state of the
Haha.. Agree.. Apologies for the wording again (Norwegians and their norweglish and weird translated sentences). What I was trying to articulate was rather the handoff of knowledge and control by doing
Got it. I fully understand there is a lot of discussions / pros / cons weighted in when designing something like this that I am missing :) Adds some limitations, but has some great benefits(!)
I think that would only partially solve the problem because that would only be true for the
That was a bad wording (again). What I meant was knowledge about reused / not reused.
That sounds like a great idea 👌 What I propose for now is that I research how I can to stop the adapters from updating a re-used |
@heumn aha, I see your point about "resetting" the collection view! It'll still be in a dirty state. Ok, great, I think I'm on the same page. I'm going to play with some ideas and report back. |
I guess I have found the two mistakes here.
In this way, it won't crash, but if reload button is clicked(In the sample project), it's possible the viewcontroller won't refresh, because of the incorrect way of implementation of IGListDiffable for EmbeddedSection. So make sure the EmbeddedSection's diffIdentifier and equalility function are correct. After this two steps above, it's solved. |
Nice suggestion and this is also a working fix, but in my head only a slightly dirty one. You are exploiting the flow of events that is happening (that cellForItem is called only for visible cells / cells that needs refresh) and not fixing the root problem. The previous IGListAdapter that holds a reference to the Adding documentation stating something like "if you are re-using cells, do not call |
If I want to implement the same behavior. I would make adapter a property of the (Currently, I think it's better to bind an And add a property such as //EmbeddedCollectionViewCell.swift
let adapter = IGListAdapter() , adapter.dataSource = self
var model: EmbeddedSection? {
didSet {
adapter.performUpdate...
}
}
implement IGListAdapterDataSource here //HorizontalSectionController.swift
func cellForItem(at index: Int) -> UICollectionViewCell {
let cell = collectionContext!.dequeueReusableCell(of: EmbeddedCollectionViewCell.self, for: self, at: index) as! EmbeddedCollectionViewCell
cell.model = self.model
return cell
} |
Not a bad idea 🤔 Pros: the two objects (the Cons: You are growing the IGListAdapter to now own a Since this framwork is already doing so much related to the It would have been really interesting to see the debate about re-using |
Ok I think it just clicked for me:
However adapters off-screen still have weak references to collection views that aren't theirs anymore! I think I have a solution to fix this: We introduce an associated object on I took your example and made it work by:
@heumn IMO this is a bug in IGListKit that we need to fix. Unfortunately that means you'll need to patch your project to get it working, which is a bummer. But the 3.0.0 milestone will include this fix. cc @jessesquires for all this. It's a good one. |
Perfect! That was somewhat one of my ideas to fix it, just wasnt smart enough to figure out how ;) Doesnt add complexity to the API nor a performance hit. In other words, sweeeeet 👌
|
@jessesquires we'll still need to do this even for #31 (in order to finish that w/out hacks) b/c how we use single section controller instances vs cell reuse (collection view gets shared between instances). |
one other thing:
Is this true? I thought 1 adapter powered a single list. Or is this only true for the "inception" (collection-view-inside-a-cell) case? |
This is only for the inception setups that wraps "collection-view-inside-a-cell" :) |
Fixed in #517 |
README
and documentationGeneral information
IGListKit
version: 2.1.0I have attached an example project where this crash occurs (based on the demo project with minor adjustment): https://www.dropbox.com/s/bnis60igq8hpa6c/crash%20in%20nested%20example.zip?dl=0
How to reproduce:
1 - Launch app
2 - Select the NestedAdapterViewController
3 - Select refresh
4 - Scroll down
5 - Select refresh (re-do steps 4-5 if it does not crash immediatly)
Crash with the classical "out of bounds"
What have I done? I have adjusted the example project to use a class (EmbeddedSection) to represent the models for the nested HorizontalSectionControllers so they in turn can embody viewmodels for their respective IGListSectionControllers (EmbeddedSectionController).
I have likely misunderstood some fundamentals about this so I am terribly sorry if this is doing something IGListKit was not intended for.
The crash appears to me to be because the IGListAdapter is getting a re-used IGListCollectionView from the EmbeddedCollectionViewCell.
My only theory to go from here is that something is retaining (retain cycle) the nested HorizontalSectionController and the adapter is trying to update a reused CollectionView that should have been deallocated when you scroll down/refresh.
Before I burn a lot more energy on this I would love be told how wrong I am for even trying to do this in the first place 😂
The text was updated successfully, but these errors were encountered: