IGListKit: Out of bounds collectionview update (crash)
- I have reviewed the
READMEand documentation - I have searched existing issues and this is not a duplicate
General information
IGListKitversion: 2.1.0- iOS version(s): 10.0
- CocoaPods/Carthage version: -
- Xcode version: 8.2.1
- Devices/Simulators affected: All
- Reproducible in the demo project? (Yes/No): No
I have attached an example project where this crash occurs (based on the demo project with minor adjustment): https://www.dropbox.com/s/bnis60igq8hpa6c/crash in nested example.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 😂
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Comments: 27 (27 by maintainers)
Ok I think it just clicked for me:
adapterinstanceadapter.collectionView = cell.collectionViewwhen the cell is reusedHowever 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
IGListCollectionViewfor the adapter. When setting the collection view, check if there is an adapter associated. If it exists, set its collection view tonil.I took your example and made it work by:
displayDelegate = selfHorizontalSectionController.init()IGListDisplayDelegateprotocollistAdapter(listAdapter:didEndDisplaying sectionController:cell:at index:)setadapter.collectionView = nil@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.
This is only for the inception setups that wraps “collection-view-inside-a-cell” 😃
@heumn thanks for reporting and including an example project! I’m going to spin through this and see what’s going on.
@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).
@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.
Hehe… I understood that, but arent you still keeping the state/knowedge about the state of the
UICollectionViewindirectly in theNSMapTable? Thus the code outlined gives you a false sense of “clean slate” for theUICollectionViewas far as I understood 😃 This seems like the root cause of the “bug” / “edge case”. Then when theperformUpdatesAnimatedis called, it tries to “connect the dots” from the previous state (before the re-use) of theUICollectionViewthat now serves as UI layer for another adapter.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
adapter.collectionViewinstead of answering to some type of protocolGot 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
UICollectionViewthat is not re-used yet and still in the pool. 🤔 The re-used ones will still crash (as they are not hidden). The old adapter will try to update aUICollectionViewthat is now “owned” by another adapterThat 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
UICollectionViewthey shouldnt update any more@heumn ack, so everything is correct, but the batch update is using the wrong
IGListCollectionView!That’s because when the update is queued inside
HorizontalSectionController.didUpdate(to object:).Here’s some proof of the issue:
I’m still rooting to fully understand the chain of events, but the biggest issue is the
adapter.performUpdates(...)call withindidUpdate(to object:), and the fact thatIGListAdapteruses the current collection view when updates is called.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.