it-swarm.com.ru

Плохой доступ к [UICollectionView setCollectionViewLayout: animated:]

Я получаю странный сбой в моем UICollectionView . Разрушающий UICollectionView встроен в ячейку UICollectionView другого UICollectionView.

Я не могу воспроизвести проблему, это иногда случается, если внутреннее UICollectionView get заново инициализируется, потому что внешний CollectionView перезагружает свои ячейки.

 com.Apple.main-thread Crashed 
 0 libobjc.A.dylib objc_msgSend + 9 
 1 UIKit - [UICollectionViewData _setLayoutAttributes: atGlobalItemIndex:] + 60 
 2 UIKit __45D [UICollection validateLayoutInRect:] _ block_invoke_0 + 668 
 3 UIKit - [UICollectionViewData validateLayoutInRect:] + 1408 
 4 UIKit - [UICollectionViewData layoutAttributesForElementsInRect:] + 82 
 5 UIKit - [. 6 MyApp BSCTopnewsCollectionView.m линии 52 - [BSCTopnewsCollectionView setupBSCTopnewsCollectionView] .__ 7 MyApp BSCTopnewsCollectionView.m линия 27 - [BSCTopnewsCollectionView setWeakDelegatePointer:]. .__ 8 Myapp BSCFrontPageViewController.m линии 550 - [BSCFrontPageViewController CollectionView: cellForItemAtIndexPath:].. .__ 9 UIKit - [UICollectionView _createPreparedCellForItemAtIndexPath: withLayoutAttributes:] + 252 
 10 UIKit - [UICollectionView _updateVisibleCellsNow:] + 2672 
 11 UIKit - [UICollect ionView layoutSubviews] + 214 
 12 UIKit - [UIView (CALayerDelegate) layoutSublayersOfLayer:] + 258 
 13 QuartzCore - [CALayer layoutSublayers] + 214 
 14 QuartzCore CA :: Layer :: layout_if_needed (CA :: Transaction *) + 460 
 15 QuartzCore CA :: Layer :: layout_and_display_if_needed (CA :: Transaction *) + 16 
 16 QuartzCore CA :: Context :: commit_transaction (CA :: Transaction *) + 238 
 17 QuartzCore CA :: Transaction :: commit () + 316 
 18 QuartzCore CA :: Transaction :: Observer_callback (__ CFRunLoopObserver *, без знака long, недействительный *) + 60 
 19 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBS_FIN_ UIGN_UIN_C_IN__T_WAL_WINK_IN_K_IN__T_W_K_IN_K_W_K_IN__T_W_K_IN_K_W_K_IN__T_W_K_IN_K_W_K__B_W_K__B_W_K__B_W_C___T_W_K__________.R._ 
___ .__ + 1120 
 26 MyApp main.m строка 16 main 


 Тип исключения: 
 . EXC_BAD_ACCESS .__ Код: 
 KERN_INVALID_ADDRESS в 0x158848 

То, что я делаю в строке 52 в setupBSCTopnewsCollectionView:

 BSCInfiniteLayout * infiniteLayout = [[BSCInfiniteLayout alloc] init]; 

 (строка 52) self.collectionView.collectionViewLayout = infiniteLayout; 



Редактировать: - [BSCFrontPageViewController collectionView: cellForItemAtIndexPath:]

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    if([collectionView isEqual:self.collectionView])
    {
        if(indexPath.row == 0) // Header Cell
        {
            BSCTopnewsCollectionView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:BSCHeaderReuseIdentifier forIndexPath:indexPath];
            cell.dataSource = self;
            cell.weakDelegatePointer = self;

            self.topNewsCollectionView = cell;

            return cell;
        }
        else
        {
            //create normal cells
        }
    }
    else if ([collectionView isEqual:self.topNewsCollectionView.collectionView])
    {
        BSCTopNewsHeaderCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:BSCTopNewsCellReuseIdentifier forIndexPath:indexPath];
        BSCNews *topnews = [self.topNews objectAtIndex:indexPath.row];

        [cell setEntity:topnews];

        return cell;
    }
}

Несколько пояснений к вызовам методов:

- (void)setWeakDelegatePointer:(BSCFrontPageViewController *)weakDelegatePointer
{
    _weakDelegatePointer = weakDelegatePointer;

    [self setupBSCTopnewsCollectionView];
    [self.collectionView reloadData];
}

- (void)setupBSCTopnewsCollectionView
{
    self.collectionView.delegate = self.weakDelegatePointer;
    self.collectionView.dataSource = self.weakDelegatePointer;

    BSCInfiniteLayout *infiniteLayout = [[BSCInfiniteLayout alloc] init];


    infiniteLayout.delegate = self;

    // Setup Layout
    self.collectionView.collectionViewLayout = infiniteLayout;
    self.collectionView.showsHorizontalScrollIndicator = NO;
    self.collectionView.pagingEnabled = YES;

    // Register Cells
    [self.collectionView registerNib:[UINib nibWithNibName:@"BSCTopNewsHeaderCell" bundle:nil] forCellWithReuseIdentifier:BSCTopNewsCellReuseIdentifier];
}



Edit3 : кажется, что сбой происходит только в особых случаях . Если приложение находилось в фоновом режиме, но все еще в памяти, и пользователь снова открывает его. Затем он проверяет наш API на наличие новых данных, и если он что-то обнаруживает, он загружает их и перезагружает весь external collectionView. Вот когда происходит сбой.

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


Чтобы сделать настройку немного более понятной .

29
muffe

Вытащив наш "ответ" из комментариев.

К сожалению, ни один из ответов не помог. Я закончил рефакторинг всего этого, и тогда проблема исчезла. У меня даже был довольно подробный разговор с инженером Apple DTS, который также не знал, что делать. Так что мы просто рефакторинг. Извини :/

0
muffe

Сначала перетащите UICollectionView на ваш ViewController в XIB, подключите делегат, источник данных к ViewController (это только основной ViewController)

Не используйте 2 разные ячейки пера для 1 CollectionView, потому что вы можете зарегистрировать только 1 пиб. Лучше использовать DecoView в качестве HeaderView. Создайте новый класс HeaderView: UICollectionReusableView. Этот UICollectionReusableView является подклассом UIView и может повторно использоваться внутри UICollectionView вместе с UICollectionViewCell. Теперь вы можете зарегистрировать оба типа:

[self.collectionView registerNib:[UINib nibWithNibName:@"MyCell" bundle:nil] forCellWithReuseIdentifier:@"CELL"];
[self.collectionView registerNib:[UINib nibWithNibName:@"HeaderView" bundle:nil] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HeaderCell"];

Затем вы перетаскиваете другой UICollectionView в этот HeaderView, соединяясь с IBOutlet внутри HeaderView.h. Здесь лучше установить Delegate, DataSource для этого класса для управления. Также зарегистрируйте, какой Nib Cell будет использовать этот CollectionView. Сделайте это в awakeFromNib, потому что вы регистрируете Nib до

- (void)awakeFromNib{
    [self.topCollectionView registerNib:[UINib nibWithNibName:@"TopCell" bundle:nil] forCellWithReuseIdentifier:@"TopCell"];

    [self.topCollectionView setDataSource:self];
    [self.topCollectionView setDelegate:self];
}

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

Если вы хотите знать, когда нажимаете на ячейку внутри headerView, используйте протокол customDelegate для отправки делегата ViewController при нажатии на HeaderCell. 

Это мой код, надеюсь, вы понимаете и можете применить свои данные здесь:

https://github.com/lequysang/gitfiles02/blob/master/CollectionViewWithDecorationView.Zip

4
LE SANG

Можете ли вы попробовать следующее, прокрутите главное представление uicollectionview, чтобы заголовок не показывался ... (еще лучше), затем на симуляторе попробуйте выполнить предупреждение о памяти ... используя Hardware-> Simulate memory warning ...

Это, вероятно, должно привести к переработке и удалению ячеек (uicollectionview должен уничтожить все, что не важно сейчас ... если это произойдет, заголовок будет освобожден, и вы все равно будете иметь слабую ссылку на него ... так что все, что вы делаете произойдет из-за плохого доступа ... также это "невозможно" воспроизвести на симуляторе из-за недостатка памяти (только те, которые вы создали явным образом) ..

Представление выглядит тяжелым, так что вы можете попробовать сделать это и опубликовать результаты?

1
Heavy_Bullets

Похоже, ваш делегат или представление внутренней коллекции мертво (что делает setWeakDelegatePointer: do?). Попробуйте инструмент Зомби на симуляторе, он должен пометить, если дело в зомби. Также установите «Точку останова исключений» для всех исключений в xCode (это поможет отладке, когда приложение запускается из xCode, а не из инструментов). Также проверьте вашу реализацию -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:], возможно, она освобождает это внутреннее представление коллекции при повторном использовании.

Edit: Почему бы просто не добавить ячейку заголовка в качестве заголовка, а не ячейку 0 (пример заголовка в представлении коллекции здесь )? Также проверьте (просто чтобы убедиться, что это не является причиной сбоя) ваши идентификаторы повторного использования, когда вы создаете обычные ячейки для внешнего представления коллекции. Также периодически отправляйте мем-предупреждения при отладке на симуляторе.

Кроме того, зачем использовать другое представление коллекции для заголовка? Вы можете использовать что-то похожее на iCarousel для такой раскладки.

1
Timur Kuchkarov