【问题标题】:UICollectionView crash on unhighlightAllItemsUICollectionView 在 unhighlightAllItems 上崩溃
【发布时间】:2013-09-26 15:48:35
【问题描述】:

我收到了几个与 iOS 7 中的 UICollectionView 相关的崩溃报告。我无法始终如一地重新创建此崩溃。

Exception Type:  SIGSEGV
Exception Codes: SEGV_ACCERR at 0x91c4392b
Crashed Thread:  0

Application Specific Information:
*** Terminating app due to uncaught exception '', reason: ''

Thread 0 Crashed:
0   libobjc.A.dylib                     0x39dd2b26 objc_msgSend + 6
1   UIKit                               0x31fd5eef -[UICollectionView cellForItemAtIndexPath:] + 111
2   UIKit                               0x32060bfd -[UICollectionView _unhighlightItemAtIndexPath:animated:notifyDelegate:] + 149
3   UIKit                               0x32383947 -[UICollectionView _unhighlightAllItems] + 151
4   UIKit                               0x3205f9fb -[UICollectionView touchesBegan:withEvent:] + 367
5   UIKit                               0x31fcb101 forwardTouchMethod + 233
6   UIKit                               0x31fcb101 forwardTouchMethod + 233
7   UIKit                               0x31e3be4b _UIGestureRecognizerUpdate + 5523
8   UIKit                               0x31e73c41 -[UIWindow _sendGesturesForEvent:] + 773
9   UIKit                               0x31e735e7 -[UIWindow sendEvent:] + 667
10  UIKit                               0x31e48a25 -[UIApplication sendEvent:] + 197
11  UIKit                               0x31e47221 _UIApplicationHandleEventQueue + 7097
12  CoreFoundation                      0x2f69e18b __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
13  CoreFoundation                      0x2f69d6e1 __CFRunLoopDoSources0 + 341
14  CoreFoundation                      0x2f69be4f __CFRunLoopRun + 623
15  CoreFoundation                      0x2f606ce7 CFRunLoopRunSpecific + 523
16  CoreFoundation                      0x2f606acb CFRunLoopRunInMode + 107
17  GraphicsServices                    0x342f4283 GSEventRunModal + 139
18  UIKit                               0x31ea8a41 UIApplicationMain + 1137
19  JackThreadsIpad                     0x000922b7 main (main.m:16)

应用程序中的 UICollectionViewCells 共享一个管理突出显示的通用超类。当单元格突出显示时,alpha 会发生变化。

- (void)setHighlighted:(BOOL)highlighted {
    [super setHighlighted:highlighted];

    if (highlighted) {
        self.alpha = 0.8;
    } else {
        self.alpha = 1.0;
    }
}

调用 [super setHighlighted:highlighted] 会导致这样的崩溃吗?该应用程序是使用 XCode 4 编译和提交的,并且仅在 iOS 7 上发生。任何其他建议来找出发生这种情况的位置。感谢您的帮助。

编辑: 我能够在调试器中捕捉到这一点,但它仍然不能始终如一地重现。崩溃是:

[NSIndexPath section] message sent to deallocated instance XXXXXXXX

【问题讨论】:

  • 你在 setHighlighted 和 cellForItemAtIndexPath 中添加断点了吗?当你通过时会发生什么,它在哪里崩溃。
  • 我在我的开发环境中没有看到这个崩溃——这个和类似的崩溃报告来自 iTunes Connect 和 Crittercism
  • 对不起,我错过了你说你不能复制它的那一行
  • 我也看到了这个崩溃,我可以重现它。这似乎与您是否在单元格中覆盖 setHighlighted 然后在 UICollectionView 上重复调用 reloadData 有关,但我仍然没有找到解决方法。在 iOS 6 中不会发生。
  • 得到完全相同的崩溃。在我的收藏视图中非常快速地滚动时发生。它似乎与突出显示无关,因为我没有覆盖任何突出显示方法。

标签: ios objective-c ios7 uicollectionview


【解决方案1】:

如果您在用户拖动视图时调用 reloadData,这可能就是原因。

我遇到了与此相关的崩溃报告,并通过将 reloadData 调用延迟到用户完成滚动视图之后“修复”了该问题。例如。创建一个封装的方法,而不是直接调用 reloadData。

- (void)updateData {
     if (self.collectionView.isTracking) {
         self.updateDataOnScrollingEnded = YES;
     } else {
         [self.collectionView reloadData];
     }
}

然后当滚动结束时,从滚动视图的委托方法中调用 updateData 方法(如果需要)。

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    if (!decelerate) {
        [self scrollViewStopped:scrollView];
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self scrollViewStopped:scrollView];
}

- (void)scrollViewStopped:(UIScrollView *)scrollView
{
    if (self.updateDataOnScrollingEnded) {
         [self updateData];
         self.updateDataOnScrollingEnded = NO;
     }
}

我的猜测是,collectionView 内部某处对突出显示单元格的 indexPath 的引用很弱,调用 reload 将释放该 indexPath。当 collectionView 然后尝试取消突出显示单元格时,它会崩溃。

编辑:

正如下面的 cmets 所述,这个“解决方案”有一些缺陷。在进一步调查该问题时,在我的情况下,问题似乎与在拖动集合视图期间在主线程上排队的多个 reloadData 调用有关。当只有一个 reloadData 调用时,一切都很好,但只要有不止一个 - 崩溃!

由于我的 collectionView 中始终只有一个部分,因此我将 reloadData 调用替换为

reloadSections:[NSIndexSet indexSetWithIndex:0]

但是,这会导致单元格快速淡出并再次淡入,我使用以下方法避免了这种情况(作为集合视图上的类别可能会更好)

- (void)reloadCollectionView:(UICollectionView *)collectionView animated:(BOOL)animated
{
    [UIView setAnimationsEnabled:animated];
    [collectionView performBatchUpdates:^{
        [collectionView reloadSections:[NSIndexSet indexSetWithIndex:0]];
    } completion:^(BOOL finished) {
        [UIView setAnimationsEnabled:YES];
    }];
}

到目前为止,这对我来说效果很好,它还允许在滚动时实际更新数据。

【讨论】:

  • 我认为这解决了问题......当集合视图开始跟踪时,它会突出显示一个单元格,但是当调用 reloadData 时,即使跟踪停止,它也会保留突出显示的状态。那时,在未来的某个时候,旧的突出显示的索引路径将导致崩溃。不幸的是,对于您的解决方案,跟踪可以开始和停止而无需拖动,这意味着您将永远不会得到任何回调来实际调用 reloadData (并且由于拖动的开始通常不会突出显示单元格,因此当 isDragging 为 YES 时这可能不是问题)。
  • 关于解决方案的另一件事;确保所有委托和数据源方法都准备好接受与旧数据相关的 NSIndexPaths,直到实际调用 -reloadData。这些当然可以在拖动和滚动期间调用,并且没有 -reloadData,集合视图仍将基于以前的数据进行调用。
  • 好点!自从我发布答案以来,我发现第二次在 collectionView 上调用 reloadData (在它有机会第一次重新加载数据之前)似乎是真正的问题。 reloadSections 方法没有这个问题,因为我开发的应用程序总是有一个部分至少有一个单元格,所以我改用 reloadSections。
  • 嗯。我在测试应用程序中尝试了该解决方案和一些变体,但我仍然可以让它崩溃,尽管它经常在枚举期间显示为改变集合而不是释放对象问题。我不确定一个重新加载如何在另一个完成之前开始(除非它们在单独的线程上,这会很糟糕)......它应该是一个同步调用。如果我在 reloadData 之前调用私有方法 _unhighlightAllItems (在上面的回溯中看到)一切都很好,但这并不是一个真正可行的方法。还是有点难过。
  • 我现在在想,如果 isTracking 已打开,是否延迟 reloadData,然后在 collectionView:didUnhighlightItemAtIndexPath: 中调用它(如果延迟),这可能会奏效。如果用户在没有开始拖动会话的情况下松手,或者如果他们开始拖动(在这种情况下 touchesCancelled: 在集合视图上调用,并且在平移时取消突出显示),则会调用取消突出显示。关于在实际调用 reloadData 之前不在数据源上安装数据的警告仍然适用。 (不确定在放手时取消突出显示后会使用哪些索引路径 didSelect: 。)
【解决方案2】:

仅识别出这段代码后不确定。但是由于崩溃信号(SIGSEGV)似乎是由于内存泄漏。您只需转到您的 Xcode 设置并在 Edit Scheme jsut 中启用 Zombie 选项,然后尝试重现您的崩溃。它将在 Xcode 的控制台中显示方法的控制器类名称或任何与崩溃相关的信息。 而且也只是尝试修改你的条件如下:-

- (void)setHighlighted:(BOOL)highlighted {

     //just comment this line or write this line to the below and check
     //[super setHighlighted:highlighted];
    if (highlighted) {
        self.alpha = 0.8;
    } else {
        self.alpha = 1.0;
    }
    [super setHighlighted:highlighted];
}

【讨论】:

    【解决方案3】:

    我遇到了这个问题,虽然崩溃略有不同。通过推迟任何 reloadData 直到突出显示被清除来修复。虽然 toostn 的建议可以解决这个问题,但能够在滚动时重新加载数据很有用,但在突出显示时没有多大意义 - 因为您的手指放在单元格上。

    实现以下 UICollectionViewDelegate 方法:

    - (BOOL)collectionView:(UICollectionView *)collectionView shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
        self.allowReload = NO;
        return YES;
    }
    
    
    - (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {
        self.allowReload = YES;
        [self reloadIfNecessary]; // calls reloadData if it is necessary to do so!
    }
    

    【讨论】:

    • 我认为这个答案更接近问题的根源。我验证了如果我从 shouldHighlight 返回 NO,那么我将不再遇到崩溃。我仍然不完全了解 reloadData 在哪里发挥作用,但就好像在触摸开始之前有未突出显示的调用被推迟,如果我在突出显示开始和结束之间调用 reloadData 并且如果我滚动得足够快这样该单元格不在屏幕上(并且可能已解除分配?)然后我得到了这个崩溃。非常感谢您的提示!
    • 编辑:说得太早了。奇怪的是,即使我从 shouldHighlight 返回 NO,我仍然会从 unhighlight 中删除崩溃...此外,将重新加载推迟到 didUnhighlightItem 之后会导致相同的异常。
    【解决方案4】:

    我在集合视图中的_unhighlightAllItems 中也遇到了这个崩溃,我使用了一个长按识别器来更改单元格的状态(但不是它们的编号),然后调用[collectionView reloadData]。就我而言,@toostn 的解决方案(使用 performBatchUpdates)效果很好。

    我还发现使用reloadItemsAtIndexPaths: 代替reloadData 也可以避免崩溃。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-15
      • 2013-11-23
      • 2015-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多