Bad Access on [UICollectionView setCollectionViewLayout:animated:]

不羁的心 提交于 2019-12-12 08:17:28

问题


I'm getting a strange crash in my UICollectionView. The crashing UICollectionView is embedded in an UICollectionView cell of another UICollectionView.

I can't reproduce the issue, it seems to happen sometimes if the inner UICollectionView get's newly initialized because the outer CollectionView is reloading it's cells.


com.apple.main-thread Crashed
0   libobjc.A.dylib     objc_msgSend + 9
1   UIKit   -[UICollectionViewData _setLayoutAttributes:atGlobalItemIndex:] + 60
2   UIKit   __45-[UICollectionViewData validateLayoutInRect:]_block_invoke_0 + 668
3   UIKit   -[UICollectionViewData validateLayoutInRect:] + 1408
4   UIKit   -[UICollectionViewData layoutAttributesForElementsInRect:] + 82
5   UIKit   -[UICollectionView setCollectionViewLayout:animated:] + 1644
6   MyApp   BSCTopnewsCollectionView.m line 52 -[BSCTopnewsCollectionView setupBSCTopnewsCollectionView]
7   MyApp   BSCTopnewsCollectionView.m line 27 -[BSCTopnewsCollectionView setWeakDelegatePointer:]
8   Myapp   BSCFrontPageViewController.m line 550 -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:]
9   UIKit   -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:] + 252
10  UIKit   -[UICollectionView _updateVisibleCellsNow:] + 2672
11  UIKit   -[UICollectionView layoutSubviews] + 214
12  UIKit   -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 258
13  QuartzCore  -[CALayer layoutSublayers] + 214
14  QuartzCore  CA::Layer::layout_if_needed(CA::Transaction*) + 460
15  QuartzCore  CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 16
16  QuartzCore  CA::Context::commit_transaction(CA::Transaction*) + 238
17  QuartzCore  CA::Transaction::commit() + 316
18  QuartzCore  CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 60
19  CoreFoundation  __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 20
25  UIKit   UIApplicationMain + 1120
26  MyApp   main.m line 16 main 


Exception Type:
    EXC_BAD_ACCESS
Code:
    KERN_INVALID_ADDRESS at 0x158848 

What I'm doing in line 52 in setupBSCTopnewsCollectionView is

BSCInfiniteLayout *infiniteLayout = [[BSCInfiniteLayout alloc] init];    

(line 52) self.collectionView.collectionViewLayout = infiniteLayout;



Edit: -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:]
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    if([collectionView isEqual:self.collectionView])
    {
        if(indexPath.row == 0) // Header Cell
        {
            BSCTopnewsCollectionView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:BSCHeaderReuseIdentifier forIndexPath:indexPath];
            cell.dataSource = self;
            cell.weakDelegatePointer = self;

            self.topNewsCollectionView = cell;

            return cell;
        }
        else
        {
            //create normal cells
        }
    }
    else if ([collectionView isEqual:self.topNewsCollectionView.collectionView])
    {
        BSCTopNewsHeaderCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:BSCTopNewsCellReuseIdentifier forIndexPath:indexPath];
        BSCNews *topnews = [self.topNews objectAtIndex:indexPath.row];

        [cell setEntity:topnews];

        return cell;
    }
}

A few clarifications for the method calls there:

- (void)setWeakDelegatePointer:(BSCFrontPageViewController *)weakDelegatePointer
{
    _weakDelegatePointer = weakDelegatePointer;

    [self setupBSCTopnewsCollectionView];
    [self.collectionView reloadData];
}

- (void)setupBSCTopnewsCollectionView
{
    self.collectionView.delegate = self.weakDelegatePointer;
    self.collectionView.dataSource = self.weakDelegatePointer;

    BSCInfiniteLayout *infiniteLayout = [[BSCInfiniteLayout alloc] init];


    infiniteLayout.delegate = self;

    // Setup Layout
    self.collectionView.collectionViewLayout = infiniteLayout;
    self.collectionView.showsHorizontalScrollIndicator = NO;
    self.collectionView.pagingEnabled = YES;

    // Register Cells
    [self.collectionView registerNib:[UINib nibWithNibName:@"BSCTopNewsHeaderCell" bundle:nil] forCellWithReuseIdentifier:BSCTopNewsCellReuseIdentifier];
}



Edit3: The crash only seems to occur in special occasions. If the app was in the background, but still in memory and the user opens it up again. It then checks our API for new data, and if it found something will load them and reload the whole outer collectionView. Thats when the crash occurs.

If the CollectionView is reloaded while the app is running without being in the background in the beginning everything is fine.


To make the setup a bit more clear.


回答1:


First, drag and drop a UICollectionView to your ViewController in XIB, hook up Delegate, datasource to ViewController(this is only main ViewController)

Don't use 2 different nib cell for 1 CollectionView, because you can only register 1 Nib. Better use DecorationView as HeaderView. Create new class HeaderView : UICollectionReusableView. This UICollectionReusableView is subclass of UIView can reuse inside UICollectionView together with UICollectionViewCell. Now you can register both type:

[self.collectionView registerNib:[UINib nibWithNibName:@"MyCell" bundle:nil] forCellWithReuseIdentifier:@"CELL"];
[self.collectionView registerNib:[UINib nibWithNibName:@"HeaderView" bundle:nil] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderCell"];

Next, you drag another UICollectionView to this HeaderView, hook up with IBOutlet inside HeaderView.h. Here, it's better to set Delegate, DataSource to this class for control. Also register what Nib Cell this CollectionView will use. Do it in awakeFromNib because you registerNib before

- (void)awakeFromNib{
    [self.topCollectionView registerNib:[UINib nibWithNibName:@"TopCell" bundle:nil] forCellWithReuseIdentifier:@"TopCell"];

    [self.topCollectionView setDataSource:self];
    [self.topCollectionView setDelegate:self];
}

It's will work well, if you store your datasource outside, just make another property and assign to it, then use for return inside datasource here.

If you want to know when click on Cell inside headerView, Use a customDelegate protocol to send delegate to ViewController when click on HeaderCell.

This is my code, hope you understand and can apply your data here:

https://github.com/lequysang/gitfiles02/blob/master/CollectionViewWithDecorationView.zip




回答2:


Looks like your delegate or inner collection view is dead(what does setWeakDelegatePointer: do?). Try Zombies instrument on simulator, it should flag if zombies are the case. Also set "Exceptions breakpoint" for all exceptions in xCode(will aid your debugging when app is run from xCode and not instruments). Also check your -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:] implementation, it may be releasing that inner collection view on reuse.

Edit: Why not just add header cell as header and not the cell 0(example of header in collection view here)? Also check(just to make sure it's not a reason of crash) your reuse identifiers when you are creationg normal cells for outer collection view. Also send mem warnings periodically when debugging on simulator.

Also, why use another collection view for header? You can use something similar to iCarousel for such layout.




回答3:


Can you try the following, scroll the main uicollectionview so the header is not showing... (further better) then on simulator try to perform a memory warning... using Hardware->Simulate memory warning...

This should probably create a cell recycling and deletion (the uicollectionview should destroy anything that is not important right now... if this happens, the header will become deallocated and you still hold a weak reference to it... so anything that you do will happens because of bad access... also this is "imposible" to reproduce on the simulator due to lack of memory warning (only the ones that you create in a explicit way)..

The view looks heavy so can you try doing this and post the results?




回答4:


Pulling our "Answer" out of the comments.

Sadly none of the answers helped. I ended up refactoring the whole thing and then the problem disappeared. I even had an pretty detailed talk with an apple DTS engineer who also didn't knew what to do. So we just refactored. Sorry :/



来源:https://stackoverflow.com/questions/18189311/bad-access-on-uicollectionview-setcollectionviewlayoutanimated

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!