29

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.
muffe
  • 2,285
  • 1
  • 19
  • 23
  • Have you tried with zombies on? – Macmade Aug 15 '13 at 11:29
  • Can you post the relevant methods from `BSCInfiniteLayout`? Specifically, `-validateLayoutInRect:` and `-layoutAttributesForElementsInRect:`. – Caleb Aug 15 '13 at 15:44
  • @Macmade I did, but I can't reproduce the issue when the debugger is running. – muffe Aug 16 '13 at 07:00
  • @Caleb Both methods are not overridden in my implementation. – muffe Aug 16 '13 at 07:01
  • It's hard to tell only from this crashlog what's happening. If you could post more code for this, answers would be more helpful I think. – Timur Kuchkarov Aug 19 '13 at 04:53
  • @TimurKuchkarov What kind of code are you looking for? I'm glad to post anything that could be helpful. – muffe Aug 19 '13 at 07:45
  • In my opinion the problem is when layouting cells, are you sure that you are using two different instances of collection flow? and as Macmade said have you tried looking for zombies? – Andrea Aug 19 '13 at 08:02
  • can u show me cellForRow Method – Prince Kumar Sharma Aug 19 '13 at 08:07
  • BSCTopnewsCollectionView is header cell? as u defined if(indexPath.row == 0) // Header Cell then what is BSCTopNewsHeaderCell? can we have small talk on chat.I need some more clarification – Prince Kumar Sharma Aug 19 '13 at 09:25
  • BSCTopnewsCollectionView is a UICollectionViewCell that contains a UICollectionView. BSCTopNewsHeaderCell is the cell that is used in the BSCTopnewsCollectionView UICollectionView. – muffe Aug 19 '13 at 09:38
  • Added a screenshot @Hayaßusa – muffe Aug 19 '13 at 09:46
  • yeah BSCTopNewsHeaderCell is subclass of CollectionViewCell containing collectionView with no of items in it. right?Now clarified what u r trying to do. – Prince Kumar Sharma Aug 19 '13 at 09:48
  • BSCTopnewsCollectionView is a subclass of UICollectionViewCell containing UICollectionView with a number of BSCTopNewsHeaderCells in it. – muffe Aug 19 '13 at 09:52
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/35736/discussion-between-hayassusa-and-muffe2k) – Prince Kumar Sharma Aug 19 '13 at 09:52
  • Added some more explanations when the app crashes. – muffe Aug 20 '13 at 09:06
  • 3
    I have exactly the same issue. Anyone has found a solution to this? The first answer is just a different implementation that could work but what if we don't work with Xibs? – nicolas Nov 13 '13 at 10:46
  • @nicolas 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 :/ – muffe Sep 01 '14 at 15:35

4 Answers4

4

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

LE SANG
  • 10,955
  • 7
  • 59
  • 78
  • At this moment the HeaderView is not only a view anymore it's acting like a ViewController. It's not good to create a design like that. The ViewController is managing the data between the model and the view. This is not valid for MVC. Your approach is more like a MVCC and that's not best way to to this. – mariusLAN Aug 22 '13 at 12:40
  • It's a DecorationView,header is part of UICollectonView, now a ViewController.Only 1ViewController in my code. Have you check it? @mariusLAN – LE SANG Aug 22 '13 at 13:16
  • Yeah i checked the code but the HeaderView is not acting as a View only. It's more acting like a Controller. It's not MVC conform. – mariusLAN Aug 22 '13 at 13:35
  • See my edit. Check Apple document and WWDC 2012 video about DecorationView – LE SANG Aug 22 '13 at 13:44
  • Delegate and Datasource is a protocol, you can set it to a class(Can be subclass of UIView). Not always set it to a ViewController – LE SANG Aug 22 '13 at 13:50
1

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.

Timur Kuchkarov
  • 1,155
  • 7
  • 21
  • I added some code. I already tried to debug the code with zombies, but I cant reproduce the error with the debugger running. – muffe Aug 19 '13 at 08:20
  • Try to send mem warnings to simulator(from top menu). Maybe this is the reason for recreation of top collection view and (probably)as old delegates are not cleared, we try to access deallocated collection view. – Timur Kuchkarov Aug 19 '13 at 15:29
  • Also make sure that if you add observers somewhere, you remove them on deallocation. – Timur Kuchkarov Aug 19 '13 at 16:17
1

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?

Heavy_Bullets
  • 518
  • 1
  • 4
  • 13
0

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 :/

muffe
  • 2,285
  • 1
  • 19
  • 23
  • I was having a similar issue, my collection view didn't have a superview at the time the layout was set and caused the crash. Might not be related to yours, but hopefully it helps someone. Cheers. – ohr Jan 30 '18 at 15:50