Mal acceso en [UICollectionView setCollectionViewLayout: animado:]


Estoy teniendo un extraño accidente en mi UICollectionView. El bloqueo de UICollectionView está incrustado en una celda de UICollectionView de otra UICollectionView.

No puedo reproducir el problema, parece suceder a veces si el UICollectionView interno se inicializa nuevamente porque el collectionView externo está recargando sus celdas.


com.apple.main-thread Crashed
0   libobjc.A.dylib     objc_msgSend + 9
1   UIKit   -[UICollectionViewData _setLayoutAttributes:atGlobalItemIndex:] + 60
2   UIKit   __45-[UICollectionViewData validateLayoutInRect:]_block_invoke_0 + 668
3   UIKit   -[UICollectionViewData validateLayoutInRect:] + 1408
4   UIKit   -[UICollectionViewData layoutAttributesForElementsInRect:] + 82
5   UIKit   -[UICollectionView setCollectionViewLayout:animated:] + 1644
6   MyApp   BSCTopnewsCollectionView.m line 52 -[BSCTopnewsCollectionView setupBSCTopnewsCollectionView]
7   MyApp   BSCTopnewsCollectionView.m line 27 -[BSCTopnewsCollectionView setWeakDelegatePointer:]
8   Myapp   BSCFrontPageViewController.m line 550 -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:]
9   UIKit   -[UICollectionView _createPreparedCellForItemAtIndexPath:withLayoutAttributes:] + 252
10  UIKit   -[UICollectionView _updateVisibleCellsNow:] + 2672
11  UIKit   -[UICollectionView 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*, unsigned long, void*) + 60
19  CoreFoundation  __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 20
25  UIKit   UIApplicationMain + 1120
26  MyApp   main.m line 16 main 


Exception Type:
    EXC_BAD_ACCESS
Code:
    KERN_INVALID_ADDRESS at 0x158848 

Lo que estoy haciendo en la línea 52 en setupBSCTopnewsCollectionView es

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

(line 52) self.collectionView.collectionViewLayout = infiniteLayout;



Editar: - [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;
    }
}

Algunas aclaraciones para el método llama allí:

- (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: El choque solo parece ocurrir en ocasiones especiales. Si la aplicación estaba en segundo plano, pero todavía en la memoria y el usuario la abre de nuevo. Luego revisa nuestra API para nuevos datos, y si encuentra algo los cargará y recargará toda la collectionView externa. Es cuando el choque ocurrir.

Si la collectionView se vuelve a cargar mientras la aplicación se está ejecutando sin estar en segundo plano al principio, todo está bien.


Para que la configuración sea un poco más clara.
Author: muffe, 2013-08-12

4 answers

Primero, arrastre y suelte un UICollectionView a su ViewController en XIB, hook up Delegate, datasource to ViewController(esto es solo ViewController principal)

No use 2 celdas de plumín diferentes para 1 collectionView, porque solo puede registrar 1 plumín. Mejor uso DecorationView como headerView. Crear una nueva clase headerView: UICollectionReusableView. Este UICollectionReusableView es una subclase de UIView que puede reutilizarse dentro de UICollectionView junto con UICollectionViewCell. Ahora puedes registrar ambos tipos:

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

A continuación, arrastra otro UICollectionView a este headerView, conéctate con IBOutlet dentro de headerView.h. Aquí, es mejor establecer Delegate, DataSource a esta clase para el control. También registre qué celda Nib usará esta collectionView. Hágalo en awakeFromNib porque usted registerNib antes

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

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

Funcionará bien, si almacena su fuente de datos fuera, simplemente cree otra propiedad y asígnela, luego use para devolver dentro de la fuente de datos aqui.

Si desea saber cuándo hacer clic en la celda dentro de headerView, Use un protocolo customDelegate para enviar delegado a ViewController cuando haga clic en HeaderCell.

Este es mi código, espero que entiendas y puedas aplicar tus datos aquí:

Https://github.com/lequysang/gitfiles02/blob/master/CollectionViewWithDecorationView.zip

 4
Author: LE SANG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 13:42:31

Parece que tu vista delegada o colección interna está muerta (¿qué hace setWeakDelegatePointer: do?). Trate de Zombies instrumento en el simulador, debe marcar si los zombies son el caso. También establezca "Punto de interrupción de excepciones" para todas las excepciones en xCode(ayudará a su depuración cuando la aplicación se ejecuta desde xCode y no desde instrumentos). También revise su implementación -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:], puede estar liberando esa vista de colección interna en la reutilización.

Editar: ¿Por qué no simplemente agregar la celda de encabezado como encabezado y no la celda 0 (ejemplo de encabezado en la vista de colección aquí)? También verifique (solo para asegurarse de que no sea una razón de bloqueo) sus identificadores de reutilización cuando esté creando celdas normales para la vista de colección externa. También envíe avisos mem periódicamente al depurar en el simulador.

Además, ¿por qué usar otra vista de colección para encabezado? Puede usar algo similar a iCarousel para tal diseño.

 1
Author: Timur Kuchkarov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-19 15:21:08

Puede intentar lo siguiente, desplácese por la vista principal de la colección para que el encabezado no se muestre... (aún mejor) entonces en el simulador tratar de realizar una advertencia de memoria... usando Hardware - > Simular advertencia de memoria...

Esto probablemente debería crear un reciclaje y eliminación de celdas (el uicollectionview debería destruir cualquier cosa que no sea importante en este momento... si esto sucede, el encabezado se desasignará y aún tendrá una referencia débil a él... así que cualquier cosa que hagas sucederá debido a un mal acceso... también esto es "imposible" de reproducir en el simulador debido a la falta de advertencia de memoria (solo los que se crean de manera explícita)..

La vista parece pesada, así que ¿puedes intentar hacer esto y publicar los resultados?

 1
Author: Heavy_Bullets,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-20 22:36:29

Sacando nuestra "Respuesta" de los comentarios.

Lamentablemente ninguna de las respuestas ayudó. Terminé refactorizando todo y luego el problema desapareció. Incluso tuve una charla bastante detallada con un ingeniero de Apple DTS que tampoco sabía qué hacer. Así que acabamos de refactorizar. Lo sentimos: /

 0
Author: muffe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-06 13:09:33