2013-09-26 7 views
8

У меня появилось несколько отчетов о сбоях, связанных с UICollectionView в iOS 7. Я не могу последовательно воссоздать этот сбой.UICollectionView crash on unhighlightAllItems

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 в доле приложении общий суперкласс, который управляет подсветку. Когда ячейка подсвечена, альфа меняется.

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

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

Могло называться [super setHighlighted: highlight] вызвать сбои, подобные этому? Приложение было скомпилировано и отправлено с XCode 4 и происходит только на iOS 7. Любые другие предложения, чтобы выяснить, где это происходит. Спасибо за вашу помощь.

Редактировать: Я смог поймать это в отладчике, но он все еще не воспроизводится последовательно. Авария:

[NSIndexPath section] message sent to deallocated instance XXXXXXXX 
+0

Вы добавили точки останова в setHighlighted и cellForItemAtIndexPath? Что происходит, когда вы проходите, где он падает. – Tim

+0

Я не видел этого сбоя в моей среде разработки - это и подобные отчеты о сбоях пришли через iTunes Connect и Crittercism –

+0

Извините, я пропустил линию, в которой вы сказали, что не можете ее воспроизвести – Tim

ответ

1

Не уверен, идентифицировав только этот фрагмент кода. Но, как сигнал сбоя (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]; 
} 
4

Если вы звоните 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; 
    } 
} 

Я думаю, что существует слабая ссылка на indexPath в выделенной ячейке где-то внутри CollectionView, и что вызов перезарядки будет dealloc что indexPath. Когда коллекцияView затем пытается высвечивать ячейку, она падает.

EDIT:

Как уже упоминалось в комментариях ниже, это "решение" имеет некоторые недостатки. При дальнейшем изучении проблемы кажется, что в моем случае проблема заключалась в том, что при перетаскивании представления коллекции несколько вызовов очереди 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]; 
    }]; 
} 

до сих пор это работает хорошо для меня, и это позволяет также данные на самом деле будет обновляться при прокрутке.

+0

Я думаю, что это вызывает проблему ... когда представление коллекции начинает отслеживать, оно будет выделять ячейку, но когда вызывается reloadData, кажется, что выделенный статус сохраняется, даже когда отслеживание останавливается. В этот момент, когда-нибудь в будущем, старый выделенный путь индекса приведет к сбою. К сожалению, для вашего решения отслеживание может начинаться и останавливаться без перетаскивания, когда вы начинаете, что означает, что вы никогда не получите никаких обратных вызовов, чтобы на самом деле вызвать reloadData (и, поскольку начало перетаскивания обычно не подчеркивает ячейки, это, вероятно, не проблема, когда isDragging есть ДА). –

+0

Еще одно замечание о решении; убедитесь, что все методы делегата и источника данных готовы принять NSIndexPaths, относящиеся к старым данным, до тех пор, пока на самом деле не будет вызван -reloadData. Они могут, безусловно, быть вызваны во время перетаскивания и прокрутки, и без -reloadData, просмотр коллекции все равно будет основывать свои вызовы на предыдущих данных. –

+0

Хорошие баллы! Поскольку я опубликовал ответ, я обнаружил, что вызов reloadData в коллекцииView во второй раз (прежде чем он имел возможность перезагрузить данные в первый раз), казалось, был реальной проблемой. У метода reloadSections не было этой проблемы, и поскольку в приложении, которое я разработал, всегда был один раздел с хотя бы одной ячейкой, я переключился на использование reloadSections. – toostn

1

У меня была эта проблема, хотя и немного другая авария. Исправлено, удерживая любую 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! 
} 
+0

Я думаю, что этот ответ становится ближе к корню проблемы. Я подтвердил, что, если я верну НЕТ из ifHighlight, я больше не получаю крах. Я до сих пор не совсем понимаю, где вступает игра reloadData, но это как если бы до того, как началось касание, были отложены отсроченные вызовы, и если я вызову reloadData между временем начала и окончания подсветки И если я прокручусь достаточно быстро, что ячейка выключена (и предположительно dealloc'd?), тогда я получаю этот крах. Большое спасибо за подсказку! – tyler

+0

Редактировать: Говорил слишком рано. Как ни странно, я все еще получаю крах, вырывающий из бессознательного, даже если я возвращаю NO из ifHighlight ... Кроме того, откладывание перезагрузки после didUnhighlightItem приводит к тому же исключению. – tyler

0

Я также имел эту аварию в _unhighlightAllItems в целях сбора, где я использовал долгое нажатие распознаватель, который изменяет состояние клеток (но не их количество), а затем называемую [collectionView reloadData]. В моем случае решение от @toostn (с использованием performBatchUpdates) отлично работает.

Я также обнаружил, что использование reloadItemsAtIndexPaths: вместо reloadData также предотвращает крушение.