web-dev-qa-db-fra.com

Mauvais accès sur [UICollectionView setCollectionViewLayout: animé:]

Un incident étrange se produit dans mon UICollectionView. Le crash de UICollectionView est incorporé dans une cellule UICollectionView d'un autre UICollectionView.

Je ne parviens pas à reproduire le problème. Cela semble parfois se produire si le fichier UICollectionView get interne est nouvellement initialisé, car CollectionView externe recharge ses cellules.

 
 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 validateLayoutInRect:] + 1408 [.________.]. ] 5 UIKit - [UICollectionView setCollectionViewLayout: animée:] + 1644 
 6 MyApp BSCTopnewsCollectionView.m line 52 - [BSCTopnewsCollectionView] ____.] 8 Myapp BSCFrontPageViewController.m line 550 - [BSCFrontPageViewControll collectionVue: cellForItIndexPath:] 
 9UIKalection (c.-à-d. eVisibleCellsNow:] + 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 :: Contexte :: commit_transaction (CA :: Transaction *) + 238 
 17 QuartzCore CA :: Transaction :: commit () + 316 
 18 QuartzCore CA :: Transaction: : observer_callback (__ CFRunLoopObserver *, long non signé, void *) + 60 
 19 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 20 [.____. 25.... .] 
 
 Type d'exception: 
 EXC_BAD_ACCESS 
 Code: 
 KERN_INVALID_ADDRESS à 0x158848 

Ce que je fais à la ligne 52 dans setupBSCTopnewsCollectionView est

 BSCInfiniteLayout * infiniteLayout = [[BSCInfiniteLayout alloc] init]; 
 
 (ligne 52) self.collectionView.collectionViewLayout = infiniteLayout; 



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

Quelques précisions sur les appels de méthodes:

- (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 : Le blocage semble ne se produire que dans des occasions spéciales. Si l'application était en arrière-plan, mais toujours en mémoire et que l'utilisateur l'ouvrait à nouveau. Il vérifie ensuite les nouvelles données dans notre API et s’il trouve quelque chose, il les chargera et rechargera l’ensemble outer collectionView. C'est quand le crash se produit.

Si CollectionView est rechargé alors que l'application est en cours d'exécution sans être en arrière-plan au début, tout va bien.


Pour rendre la configuration un peu plus claire.

29
muffe

Tirant notre "réponse" sur les commentaires.

Malheureusement, aucune des réponses n'a aidé. J'ai fini par refactoriser le tout, puis le problème a disparu. J'ai même eu une conversation assez détaillée avec un ingénieur Apple DTS qui ne savait pas non plus quoi faire. Nous avons donc simplement refactored. Pardon :/

0
muffe

Tout d’abord, faites glisser un UICollectionView sur votre ViewController dans XIB, connectez délégué, source de données à ViewController (ce n’est que le ViewController principal)

N'utilisez pas 2 cellules de nib différentes pour 1 CollectionView, car vous ne pouvez enregistrer qu'un seul Nib. Mieux utiliser DecorationView comme HeaderView. Créez une nouvelle classe HeaderView: UICollectionReusableView. UICollectionReusableView est une sous-classe d'UIView pouvant être réutilisée dans UICollectionView avec UICollectionViewCell. Maintenant, vous pouvez enregistrer les deux types:

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

Ensuite, vous faites glisser un autre UICollectionView vers ce HeaderView, vous vous connectez avec IBOutlet dans HeaderView.h. Ici, il est préférable de définir Delegate, DataSource à cette classe pour le contrôle. Enregistrez également la cellule Nib que CollectionView utilisera. Faites-le dans awakeFromNib car vous enregistrezNib avant

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

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

Cela fonctionnera bien, si vous stockez votre source de données à l'extérieur, créez simplement une autre propriété et affectez-la, puis utilisez-la pour revenir à l'intérieur de la source de données ici.

Si vous voulez savoir quand vous cliquez sur Cell dans headerView, utilisez un protocole customDelegate pour envoyer un délégué à ViewController lorsque vous cliquez sur HeaderCell. 

Ceci est mon code, j'espère que vous comprenez et pouvez appliquer vos données ici:

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

4
LE SANG

Pouvez-vous essayer ce qui suit, faites défiler la vue principale uicollection pour que l’en-tête ne soit pas affiché ...

Cela devrait probablement créer un recyclage et une suppression de cellules (uicollectionview devrait détruire tout ce qui n’est pas important pour le moment ... si cela se produit, l’en-tête sera désalloué et vous conserverez toujours une faible référence à celui-ci ... Cela se produira à cause d'un mauvais accès ... c'est aussi "impossible" de se reproduire sur le simulateur à cause d'un manque d'alerte mémoire (seulement ceux que vous créez de manière explicite) ..

La vue semble lourde alors pouvez-vous essayer de faire cela et publier les résultats?

1
Heavy_Bullets

On dirait que votre délégué ou votre vue de collection interne est morte (que fait setWeakDelegatePointer: do?). Essayez l'instrument Zombies sur simulateur, il devrait signaler si les zombies sont le cas. Définissez également le "point d'arrêt d'exceptions" pour toutes les exceptions dans xCode (facilitera le débogage lorsque l'application est exécutée à partir de xCode et non d'instruments). Vérifiez également votre implémentation -[BSCFrontPageViewController collectionView:cellForItemAtIndexPath:], il est possible que cette vue de collection interne soit publiée lors de la réutilisation.

Edit: Pourquoi ne pas simplement ajouter une cellule d'en-tête comme en-tête et non la cellule 0 (exemple d'en-tête dans la vue collection ici )? Vérifiez également (juste pour vous assurer que ce n'est pas une cause d'accident) vos identifiants de réutilisation lorsque vous créez des cellules normales pour une vue de collection externe. Également envoyer des avertissements de mémoire périodiquement lors du débogage sur le simulateur.

Aussi, pourquoi utiliser une autre vue de collection pour en-tête? Vous pouvez utiliser quelque chose de similaire à iCarousel pour une telle présentation.

1
Timur Kuchkarov