2013-08-02 4 views
1

Сценарий: 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 

ответ

1

1: Когда вы перемещаетесь от контроллера вида с представлением коллекции, вы не используете ссылки делегата и источники данных в виде коллекций, чтобы вы больше не получали запрос?

Это происходит и с табличными представлениями, объекты все еще сохраняются, но данные или источники этого не делают.

2: Я также рекомендую использовать @synchronized (self) при изменении коллекций. Так что код, который устанавливает и читает его, не сталкивается (перечислимый сбой)

+0

Да. Это была идея, которую я получил от некоторых других вопросов SO, но это не помогло. – Shinigami

+0

Пожалуйста, попробуйте предложение №2, используя @синхронизированные блоки, которые могут помочь исправить то, что известно как «условия гонки» –

+0

Хм ... Я знаю о состоянии гонки, и на нем написано «состояние гонки». Но какова структура данных, которая меняется в состоянии гонки? Мой код никогда не меняет свои массивы источников данных после того, как он изначально установил их на выход API. И нет никакого кода, «перечисляющего» их. Итак, какую коллекцию я должен поставить в @synchronized? – Shinigami

Смежные вопросы