【问题标题】:App Crashes When Navigating Away from Loading Collection View导航离开加载集合视图时应用程序崩溃
【发布时间】:2013-08-02 01:14:16
【问题描述】:

场景:UINavigationViewController 包含 UICollectionViewControllers。每个集合视图都需要从 JSON API 获取其数据,这可能需要一些时间。然后集合视图中的每个单元格向服务器发送自己的请求,获取缩略图。 (为了清楚起见,我创建了一个 sequence diagram 来展示我是如何做到这一点的,不包括缩略图请求,我已禁用它以进行调试)。

这就是代码的样子:

-(void)buildDisplayItems
{
    NSLog(@"Collection VC (object: %@) with collection (object: %@) building display items on thread %@ (main thread: %c)", self, self.showingCollection, [NSThread currentThread], [NSThread isMainThread]);
    [self.showingCollection buildDisplayItemsWithPhotoBatch:self.nextBatch++]; // <-- this waits for the server request to be made, come back and get processed into arrays before returning.
    self.photos = self.showingCollection.photos;
    self.collections = self.showingCollection.subcategories;
    // Past this point, I cannot think of how these arrays would possibly get changed to trigger the 'mutated while being enumerated' errors.
    [self.collectionView reloadData];
}

问题是,如果用户在正确的时间返回导航视图,我会收到以下错误之一:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection &lt;NSConcreteMapTable: 0x1f047c80&gt; was mutated while being enumerated.'

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection &lt;__NSSetM: 0x203f26b0&gt; was mutated while being enumerated.'

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[3]'

要使这些错误发生,我必须尝试 3-4 次选择某个项目,以便将其集合视图推送到导航堆栈上,然后快速按下后退按钮。显然,我对前两个错误的第一印象是数据源数组正在以某种方式被修改,但我写的任何东西都没有这样做。然后我想可能是每个单元格的缩略图在 cellForItemAtIndexPath 返回后加载的事实是问题,所以我禁用了它,但没有任何改变。

我散布了一些 NSLogs,发现这是在集合视图调用其委托方法时发生的。我确保添加以下内容:

-(void)viewWillDisappear:(BOOL)animated
{
    if (self.showingCollection != nil) {
        [self.showingCollection cancelOperations]; //for network requests
        NSLog(@"Collection VC (object: %@) will disappear", self);
    }
}

-(void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    self.collectionView.delegate = nil;
    self.collectionView.dataSource = nil;
    self.collectionView = nil;
}

但这无助于阻止错误。事实上,有时当崩溃发生时,并没有“Collection VC will消失”的日志信息。

另一个问题是这些错误发生并且调试器在 UIApplicationMain 行的主函数中停止(这是与'break on all exceptions'断点),所以我无法确定错误是如何被触发的。我启用了僵尸,但没有来自它们的日志消息。 (编辑:有时它实际上停在一个空线程中,说“错误:地址不包含指向目标文件中的部分的部分”)

如何设置这些集合视图,以便快速按下后退按钮不会结束世界?

编辑 在枚举时如何找出正在更改的集合是什么?

更新 我发现这个 SO 线程谈论无法获取堆栈跟踪。每次我遇到上述异常之一时,它都会给我一个堆栈跟踪,如

*** First throw call stack:
(0x327453e7 0x3a440963 0x32744ec1 0x330f72c7 0x330f769b 0x330f825b 0x330fa0c7 0x330faefd 0x33103cab 0x3498c519 0x3498d5f1 0x3498d915 0x349920d9 0x34991fe3 0x3268bacd 0x34991f97 0x3268bacd 0x34991f97 0x3268bacd 0x34991f97 0x3268bacd 0x34991f97 0x3268bacd 0x34991f97 0x3268bacd 0x34991f97 0x330fa997 0x3498bf3d 0x345c73dd 0x34303513 0x343030b5 0x34303fd9 0x343039c3 0x343037d5 0x3434a567 0x3a87febb 0x3a87fb93 0x3a898fb8 0x32fe5fc7 0x3305d24f 0x3a88d0e1 0x3a88cfa8)
libc++abi.dylib: terminate called throwing an exception

根据该线程的建议,我实现了自己的未捕获异常处理程序,并打印出以下堆栈跟踪:

    0   CoreFoundation                      0x327453ff <redacted> + 186
1   libobjc.A.dylib                     0x3a440963 objc_exception_throw + 30
2   CoreFoundation                      0x32744ec1 <redacted> + 0
3   Foundation                          0x330f72c7 <redacted> + 422
4   Foundation                          0x330f769b <redacted> + 298
5   Foundation                          0x330f825b <redacted> + 202
6   Foundation                          0x330fa0c7 <redacted> + 242
7   Foundation                          0x330faefd <redacted> + 500
8   Foundation                          0x33103cab <redacted> + 390
9   UIKit                               0x3498c519 <redacted> + 128
10  UIKit                               0x3498d5f1 <redacted> + 196
11  UIKit                               0x3498d915 <redacted> + 88
12  UIKit                               0x349920d9 <redacted> + 84
13  UIKit                               0x34991fe3 <redacted> + 182
14  CoreFoundation                      0x3268bacd CFArrayApplyFunction + 176
15  UIKit                               0x34991f97 <redacted> + 106
16  CoreFoundation                      0x3268bacd CFArrayApplyFunction + 176
17  UIKit                               0x34991f97 <redacted> + 106
18  CoreFoundation                      0x3268bacd CFArrayApplyFunction + 176
19  UIKit                               0x34991f97 <redacted> + 106
20  CoreFoundation                      0x3268bacd CFArrayApplyFunction + 176
21  UIKit                               0x34991f97 <redacted> + 106
22  CoreFoundation                      0x3268bacd CFArrayApplyFunction + 176
23  UIKit                               0x34991f97 <redacted> + 106
24  CoreFoundation                      0x3268bacd CFArrayApplyFunction + 176
25  UIKit                               0x34991f97 <redacted> + 106
26  Foundation                          0x330fa997 <redacted> + 166
27  UIKit                               0x3498bf3d <redacted> + 124
28  UIKit                               0x345c73dd <redacted> + 72
29  QuartzCore                          0x34303513 <redacted> + 214
30  QuartzCore                          0x343030b5 <redacted> + 460
31  QuartzCore                          0x34303fd9 <redacted> + 16
32  QuartzCore                          0x343039c3 <redacted> + 238
33  QuartzCore                          0x343037d5 <redacted> + 316
34  QuartzCore                          0x3434a567 <redacted> + 170
35  libsystem_c.dylib                   0x3a87febb _pthread_tsd_cleanup + 174
36  libsystem_c.dylib                   0x3a87fb93 <redacted> + 118
37  libsystem_c.dylib                   0x3a898fb8 pthread_exit + 27
38  Foundation                          0x32fe5fc7 <redacted> + 10
39  Foundation                          0x3305d24f <redacted> + 1002
40  libsystem_c.dylib                   0x3a88d0e1 <redacted> + 308
41  libsystem_c.dylib                   0x3a88cfa8 thread_start + 8

【问题讨论】:

    标签: ios objective-c api debugging uicollectionview


    【解决方案1】:

    1:当您使用集合视图离开视图控制器时,您是否在集合视图中取消了委托引用和数据源,因此您不再被查询?

    表视图也会发生这种情况,对象仍然存在,但数据或源不存在。

    2:我还建议在修改集合时使用@synchronized(self)。这样设置和读取它的代码就不会发生冲突(枚举崩溃)

    【讨论】:

    • 是的。这是我从其他一些 SO 问题中得到的想法,但没有帮助。
    • 请尝试第 2 条建议,使用 @synchronised 块可能有助于解决所谓的“竞争条件”
    • 嗯...我知道竞态条件,并且上面写满了“竞态条件”。但是,在竞争条件下发生变化的数据结构到底是什么?我的代码在最初将其设置为 API 的输出后,从未更改其数据源数组。而且也没有代码“枚举”它们。那么我应该在@synchronized 中放入什么集合呢?
    • 我认为正在发生的事情是在最初加载数据时尝试进行清理。你想通过取消或延迟一个直到另一个完成来保持同步。 @synchronised 可以做到这一点
    • 对不起...我不太明白你的意思。我有两个数组(self.photos 和 self.collections)。它们以空数组开始。然后它们被分配给其他数组。我将 @synchronized(self) 放在了作业周围,这并没有改变任何东西。我的代码也从不枚举任何东西,所以我很困惑枚举发生在哪里以及它到底在枚举什么。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-25
    • 2015-05-05
    • 2018-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多