[UICollectionView setCollectionViewLayout上的错误访问:动画:]

时间:2013-08-12 14:16:37

标签: ios objective-c uikit uicollectionview exc-bad-access

我在UICollectionView中遇到了一个奇怪的崩溃。 崩溃的UICollectionView嵌入在另一个UICollectionView的UICollectionView单元格中。

我无法重现这个问题,如果内部UICollectionView被新初始化,有时会发生这种情况,因为外部的CollectionView正在重新加载它的单元格。


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 

我在setupBSCTopnewsCollectionView的第52行中所做的是

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

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

<小时/>
编辑: - [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;
    }
}

该方法的一些说明要求:

- (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 :崩溃似乎只发生在特殊场合。 如果应用程序在后台,但仍在内存中,用户再次打开它。然后它检查我们的API以获取新数据,如果发现某些内容将加载它们并重新加载整个外部集合视图。那是发生崩溃的时候。

如果在应用程序运行时重新加载CollectionView而不是在开头的后台,一切都很好。

<小时/> 使设置更清晰一点。

4 个答案:

答案 0 :(得分:4)

首先,将UICollectionView拖放到XIB中的ViewController,将Delegate,datasource挂钩到ViewController(这只是主ViewController)

不要为1个CollectionView使用2个不同的笔尖单元格,因为您只能注册1个笔尖。最好使用DecorationView作为HeaderView。创建新类HeaderView:UICollectionReusableView。这个UICollectionReusableView是UIView的子类,可以与UICollectionViewCell一起在UICollectionView中重用。现在您可以注册两种类型:

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

接下来,将另一个UICollectionView拖到此HeaderView中,与HeaderView.h中的IBOutlet挂钩。在这里,最好将Delegate,DataSource设置为此类以进行控制。同时注册此CollectionView将使用的Nib Cell。在awakeFromNib中执行,因为您在

之前注册了Nib
- (void)awakeFromNib{
    [self.topCollectionView registerNib:[UINib nibWithNibName:@"TopCell" bundle:nil] forCellWithReuseIdentifier:@"TopCell"];

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

它会很好用,如果你将数据源存储在外面,只需创建另一个属性并分配给它,然后在这里用于返回数据源。

如果您想知道何时单击headerView中的Cell,请在单击HeaderCell时使用customDelegate协议将委托发送到ViewController。

这是我的代码,希望您理解并可以在此处应用您的数据:

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

答案 1 :(得分:1)

看起来你的委托或内部集合视图已经死了(setWeakDelegatePointer:做什么?)。在模拟器上尝试Zombies仪器,它应该标记僵尸是否是这种情况。同时设置&#34;例外断点&#34;对于xCode中的所有异常(当从xCode而不是仪器运行app时,将帮助您进行调试)。另请检查您的-[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:]实现,它可能会在重用时释放内部集合视图。

编辑:为什么不将标题单元格添加为标题而不是单元格0(集合视图here中的标题示例)?当您为外部集合视图创建正常单元格时,还要检查(只是为了确保它不是崩溃的原因)您的重用标识符。在模拟器上调试时,还要定期发送mem警告。

另外,为什么要使用标题的另一个集合视图?您可以使用与iCarousel类似的内容进行此类布局。

答案 2 :(得分:1)

你可以尝试以下方法,滚动主要的uicollectionview,这样头部没有显示...(更好)然后在模拟器上尝试执行内存警告...使用硬件 - &gt;模拟内存警告... < / p>

这应该可以创建一个单元格回收和删除(uicollectionview应该销毁任何现在不重要的东西......如果发生这种情况,标题将被取消分配,你仍然对它有一个弱引用......所以任何东西你会这样做会因为访问不当而发生...由于缺少内存警告(仅限你以明确的方式创建),这在模拟器上重现是“不可能的”。

视图看起来很重,所以你可以尝试这样做并发布结果吗?

答案 3 :(得分:0)

从评论中删除我们的“答案”。

可悲的是,没有一个答案有帮助。我最终重构了整个事情然后问题消失了。我甚至与苹果DTS工程师进行了非常详细的交谈,他也不知道该怎么做。所以我们只是重构了。对不起:/