XLPagerTabStrip icon indicating copy to clipboard operation
XLPagerTabStrip copied to clipboard

Crash by Index out of range because cachedCellWidths is not synched to viewControllers

Open always19 opened this issue 7 years ago • 23 comments

Phenomenon

Crash by Index out of range because cachedCellWidths is not synched to viewControllers

image

fatal error: Index out of range
(lldb) po cachedCellWidths
▿ Optional<Array<CGFloat>>
  ▿ some : 6 elements
    - 0 : 93.0
    - 1 : 70.5
    - 2 : 94.5
    - 3 : 102.5
    - 4 : 165.5
    - 5 : 151.5
(lldb) po indexPath
▿ 2 elements
  - 0 : 0
  - 1 : 6
(lldb) po viewControllers.count
8

Analysis

  1.  self?.reloadPagerTabStripView() // I called reloadPagerTabStripView from my ButtonBarPagerTabStripViewController subclass
    
  2.  open override func reloadPagerTabStripView() {
         super.reloadPagerTabStripView()  // super.reloadPagerTabStripView() is called before cachedCellWidths is set. So, cachedCellWidths.count is different from viewControllers.count
         ...
         cachedCellWidths = calculateWidths() 
    
  3.  // Eventually, following is called first.
     open func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: IndexPath) -> CGSize {
         guard let cellWidthValue = cachedCellWidths?[indexPath.row] else { // So, it crashes here.
    

call stack

#3	0x00000001106ddf4b in ButtonBarPagerTabStripViewController.collectionView(UICollectionView, layout : UICollectionViewLayout, sizeForItemAtIndexPath : IndexPath) -> CGSize
#4	0x00000001106de24c in @objc ButtonBarPagerTabStripViewController.collectionView(UICollectionView, layout : UICollectionViewLayout, sizeForItemAtIndexPath : IndexPath) -> CGSize ()
#5	0x00000001130bfa82 in -[UICollectionViewFlowLayout _getSizingInfosWithExistingSizingDictionary:] ()
#6	0x00000001130c1114 in -[UICollectionViewFlowLayout _fetchItemsInfoForRect:] ()
#7	0x00000001130ba07e in -[UICollectionViewFlowLayout prepareLayout] ()
#8	0x00000001130da215 in -[UICollectionViewData _prepareToLoadData] ()
#9	0x00000001130919c0 in -[UICollectionView _endItemAnimationsWithInvalidationContext:tentativelyForReordering:animator:] ()
#10	0x000000011308e75a in -[UICollectionView _updateRowsAtIndexPaths:updateAction:] ()
#11	0x00000001106dd7b7 in ButtonBarPagerTabStripViewController.cellForItems(at : [IndexPath], reloadIfNotVisible : Bool)
#12	0x00000001106dcfc4 in ButtonBarPagerTabStripViewController.updateIndicator(for : PagerTabStripViewController, fromIndex : Int, toIndex : Int, withProgressPercentage : CGFloat, indexWasChanged : Bool)
#13	0x000000010ea12122 in XXXPagerTabStripController.updateIndicator(for : PagerTabStripViewController, fromIndex : Int, toIndex : Int, withProgressPercentage : CGFloat, indexWasChanged : Bool)
#14	0x00000001106e22a1 in protocol witness for PagerTabStripIsProgressiveDelegate.updateIndicator(for : PagerTabStripViewController, fromIndex : Int, toIndex : Int, withProgressPercentage : CGFloat, indexWasChanged : Bool) -> () in conformance ButtonBarPagerTabStripViewController ()
#15	0x00000001106f40b4 in PagerTabStripViewController.updateContent()
#16	0x00000001106f4c74 in PagerTabStripViewController.reloadPagerTabStripView()
#17	0x00000001106dbdf9 in ButtonBarPagerTabStripViewController.reloadPagerTabStripView() 

Environment

  1. I'm using 7ff9ed755462fe5e4f891868bc7eafd03f8c4355 close to HEAD in master. I believe this problem is not fixed yet. I checked later commit logs.
  2. It doesn't happen every time. I don't know how I can reproduce this problem yet.

always19 avatar Jun 03 '17 02:06 always19

+1

gali8 avatar Jun 16 '17 08:06 gali8

+1

SValchyshyn avatar Dec 15 '17 08:12 SValchyshyn

+1

chayakornS avatar Jan 05 '18 04:01 chayakornS

I have the same problem, and my version (8.0.0) have the 7ff9ed7 changes :(

mcinside13 avatar Jan 13 '18 19:01 mcinside13

+1

danielstorch avatar Jan 20 '18 08:01 danielstorch

+1

dmead28 avatar Jan 29 '18 19:01 dmead28

+1

kevinmun avatar May 02 '18 02:05 kevinmun

The crash happens when the new viewControllers.count is greater that the existing viewControllers.count. The problem is that PagerTabStripViewController which is the superclass of ButtonBarPagerTabStripViewController, calls updateContent() before the new cachedCellWidths are calculated - the calculation is done in ButtonBarPagerTabStripViewController.reloadPagerTabStripView() AFTER super.reloadPagerTabStripView() is called

Here is a fix for the crash, which postpones the call to updateContent() after everything is in place:

File: ButtonBarPagerTabStripViewController.swift

add a variable:

private var shouldUpdateContent = true

replace reloadPagerTabStripView() with:

open override func reloadPagerTabStripView() {
    shouldUpdateContent = false
    super.reloadPagerTabStripView()
    shouldUpdateContent = true
        
    guard isViewLoaded else { return }
    buttonBarView.reloadData()
    cachedCellWidths = calculateWidths()
    updateContent()
    buttonBarView.moveTo(index: currentIndex, animated: false, swipeDirection: .none, pagerScroll: .yes)
}

and add this function:

open override func updateContent() {
    if shouldUpdateContent {
        super.updateContent()
    }
}

nickbit avatar Jun 24 '18 20:06 nickbit

Thanks. @nickbit! This took many hours out of my day until I came upon this. Works great!

Appswage avatar Jul 10 '18 05:07 Appswage

emm... when using @nickbit 's solution, i crashed in another place:

if !indexPathsToReload.isEmpty {
    buttonBarView.reloadItems(at: indexPathsToReload)
}

with the log

Project(74688,0x700007fb1000) malloc: Heap corruption detected, free list is damaged at 0x600001f48000
*** Incorrect guard value: 105553124933760
Project(74688,0x10e4235c0) malloc: Heap corruption detected, free list is damaged at 0x600001f48020
*** Incorrect guard value: 5134468915308744792
Project(74688,0x700007fb1000) malloc: *** set a breakpoint in malloc_error_break to debug
Project(74688,0x10e4235c0) malloc: *** set a breakpoint in malloc_error_break to debug

aalenliang avatar Nov 13 '18 03:11 aalenliang

@aalenliang I am also getting the same crash. Any update?

dasSoumen avatar Mar 07 '19 07:03 dasSoumen

@dasSoumen Have you tried call self.buttonBarView.layoutIfNeeded() before self.reloadPagerTabStripView()?

daoseng33 avatar Mar 28 '19 14:03 daoseng33

getting the same error Heap corruption detected, free list is damaged at reloadItems. if !indexPathsToReload.isEmpty { buttonBarView.reloadItems(at: indexPathsToReload) }

ShoaibPathan avatar Apr 18 '19 09:04 ShoaibPathan

@aalenliang have u fixed it the issue heap corruption

ShoaibPathan avatar Apr 18 '19 09:04 ShoaibPathan

@dasSoumen @ShoaibPathan Sorry for the late reply, i used george-lin/XLPagerTabStrip instead.

aalenliang avatar Apr 29 '19 12:04 aalenliang

I'm still getting this error... I updated this library today with cocoa pods...any help?

kevintorch avatar May 07 '19 12:05 kevintorch

Still getting this crash , Has somebody fixed this. Need help in this

PrashantAhar avatar Jul 09 '19 05:07 PrashantAhar

For me, it wasn't crashing if I call reloadPagerTabStripView() from the switch of the same page in a different container. but if I call the same method just after page open then It was crashing. Here is the workaround I found to solve this issue temporary:: Just add the delay of 3 seconds.

extension DispatchQueue {

static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
    DispatchQueue.global(qos: .background).async {
        background?()
        if let completion = completion {
            DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
                completion()
            })
        }
    }
}

}

USAGE---> SVProgressHUD.show() DispatchQueue.background(delay: 3.0, completion:{ // do something in main thread after 3 seconds reloadPagerTabStripView() SVProgressHUD.dismiss() })

dhruvalpatel-tamrd avatar Mar 25 '20 20:03 dhruvalpatel-tamrd

this project needs to fork

rromanchuk avatar Apr 14 '20 23:04 rromanchuk

updated on 24 Aug 2020 Still get error at : buttonBarView.reloadItems(at: indexPathsToReload)

any solution guys ?

minhmera avatar Aug 24 '20 08:08 minhmera

Is there any solution for this? Will appreciate it

hechukwu avatar Aug 31 '20 11:08 hechukwu

@nickbit I get 'cachedCellWidths' is inaccessible due to 'private' protection level

hechukwu avatar Aug 31 '20 11:08 hechukwu

@minhmera try adding self.buttonBarView.layoutIfNeeded() before updateContent() with @nickbit answer. It seems to resolve the issue

hechukwu avatar Aug 31 '20 12:08 hechukwu