1

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

这是代码的样子:

-(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 <NSConcreteMapTable: 0x1f047c80> was mutated while being enumerated.'

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSSetM: 0x203f26b0> 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 返回后每个单元格的缩略图被加载是问题所在,所以我禁用了它,但没有任何改变。

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

-(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 行的主函数中停止(这是与“所有异常中断”断点),所以我无法确定错误是如何被触发的。我启用了僵尸,但没有来自它们的日志消息。(编辑:有时它实际上在一个空线程中停止,说“错误:地址不包含指向目标文件中的部分的部分”)

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

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

更新 我发现这个 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
4

1 回答 1

1

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

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

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

于 2013-08-02T01:17:31.050 回答