Сценарий: UINavigationViewController содержит UICollectionViewControllers. Каждый вид коллекции должен получать свои данные из API JSON, что может занять немного времени. Затем каждая ячейка в представлении коллекции отправляет свои собственные запросы на сервер, получая миниатюрные изображения. (Для ясности я создал 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 <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 возвращается, это проблема, поэтому я отключил ее, но ничего не изменилось.
Я посыпал некоторые 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 исчезнет».
Другая проблема заключается в том, что эти ошибки происходят, и отладчик останавливается в основной функции в строке UIApplicationMain (это с точкой прерывания «break on all exceptions»), поэтому я не могу точно определить, как срабатывает ошибка. Я включил зомби, но из них нет сообщений журнала. (EDIT: Иногда он фактически останавливается в пустой ветке, говоря: «error: address не содержит раздел, который указывает на раздел в объектном файле»)
Как настроить эти виды коллекций так, чтобы нажатие кнопки «назад» быстро не заканчивает мир?
EDIT Как я могу узнать, что представляет собой коллекция, которая изменяется при перечислении?
ОБНОВЛЕНИЕ Я нашел этот поток 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
Да. Это была идея, которую я получил от некоторых других вопросов SO, но это не помогло. – Shinigami
Пожалуйста, попробуйте предложение №2, используя @синхронизированные блоки, которые могут помочь исправить то, что известно как «условия гонки» –
Хм ... Я знаю о состоянии гонки, и на нем написано «состояние гонки». Но какова структура данных, которая меняется в состоянии гонки? Мой код никогда не меняет свои массивы источников данных после того, как он изначально установил их на выход API. И нет никакого кода, «перечисляющего» их. Итак, какую коллекцию я должен поставить в @synchronized? – Shinigami