数据源方法collectionView:numberOfItemsInSection:崩溃应用程序“索引0超出空数组的范围”

Posted

技术标签:

【中文标题】数据源方法collectionView:numberOfItemsInSection:崩溃应用程序“索引0超出空数组的范围”【英文标题】:Datasource method collectionView: numberOfItemsInSection: Crashes App "index 0 beyond bounds for empty array" 【发布时间】:2014-05-05 04:02:48 【问题描述】:

我整天都在处理这个代码,所以它可能只是我忽略的一些愚蠢的东西,但我似乎无法找出问题所在,我无计可施。

-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView

    if(collectionView == self.titleBarCollectionView) 
        return 1;
    
    NSInteger photoCount = [[self.fetchedResultsController fetchedObjects] count];
    NSInteger sectionCount = (photoCount + 3) / 3;
    return sectionCount;


-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

    if(collectionView != self.titleBarCollectionView) 
        id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];
        NSInteger numberInSection = [sectionInfo numberOfObjects];
        if(numberInSection < 3) numberInSection++;
        return numberInSection;
    
    return 3;

我有一个显示用户照片的 UICollectionView。照片取自核心数据,但是集合视图中的第一个索引(项目 0,第 0 部分)被替换为“添加照片”图像,并且所有照片的 indexPaths 都进行了调整以适应这一点。它工作了好几个小时,但由于某种原因在删除应用程序(重置核心数据)后,我似乎什至无法加载视图。当它到达id sectionInfo = [self.fetchedResultsController sections][section]; 行时,它会立即崩溃并显示错误<strong>* Terminating app due to uncaught exception 'NSRangeException', reason: '*</strong> -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'。我这辈子都想不通。这是我的其余数据源方法。

-(void)configureCell:(UICollectionViewCell *)cell atIndexPath:(NSIndexPath *)indexPath

    if([cell.reuseIdentifier isEqualToString:@"Profile Title Cell"])  
        ... Irrelevant code ...
     else if(indexPath.row == 0 && indexPath.section == 0) 
        UIImageView *imageView = (UIImageView *)[cell viewWithTag:300];
        imageView.image = [UIImage imageNamed:@"plusIcon.png"];
     else 
        NSIndexPath *newIndexPath = [self newIndexPathFromIndexPath:indexPath];
        Photo *photo = [self.fetchedResultsController objectAtIndexPath:newIndexPath];
        UIImageView *imageView = (UIImageView *)[cell viewWithTag:300];

        NSData *imageData = [[NSData alloc] initWithBase64EncodedString:photo.thumbnailData options:0];
        imageView.image = [UIImage imageWithData:imageData];

        if(photo.imageData) 
            imageView.image = [UIImage imageWithData:photo.imageData];
         else if(photo.imageURL && ![photo.imageURL isEqualToString:@""]) 
            NSString *imageURL = [NSString stringWithFormat:@"%@%@", PHOTOS_BASE_URL, photo.imageURL];
            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
                [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
                self.activeThreads += 1;
                NSData *imageURLData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]];
                dispatch_async(dispatch_get_main_queue(), ^
                    photo.imageData = imageURLData;
                    [self.imageCollectionView reloadItemsAtIndexPaths:@[indexPath]];
                    self.activeThreads -= 1;
                    if(self.activeThreads < 1)
                        [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
                );
            );
        
    


-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath

    UICollectionViewCell *cell = nil;
    if(collectionView == self.titleBarCollectionView) 
        cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Profile Title Cell" forIndexPath:indexPath];
     else 
        cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Profile Image Cell" forIndexPath:indexPath];
    
    [self configureCell:cell atIndexPath:indexPath];
    return cell;


- (NSIndexPath *)newIndexPathFromIndexPath:(NSIndexPath *)indexPath

    NSInteger row = 0;
    NSInteger section = 0;
    if(indexPath.row > 0) 
        row = indexPath.row - 1;
        section = indexPath.section;
     else 
        row = 2;
        section = indexPath.section - 1;
    
    return [NSIndexPath indexPathForItem:row inSection:section];

编辑:这是我的 fetchedResultsController 设置器

- (NSFetchedResultsController *)fetchedResultsController

    if(!_fetchedResultsController) 
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];

        fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"uploadDate" ascending:YES]];

        fetchRequest.predicate = [NSPredicate predicateWithFormat:@"ownerID == %@", self.currentUser.userID];

        _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                                                        managedObjectContext:[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext
                                                                          sectionNameKeyPath:@"sectionIdentifier"
                                                                                   cacheName:nil];

        _fetchedResultsController.delegate = self;

        [_fetchedResultsController performFetch:nil];
    
    return _fetchedResultsController;

【问题讨论】:

这可能是因为您创建了 UICollectionView 的属性并忘记将其与界面生成器连接。请检查它是否已连接。 @sahebSingh 是的,它已连接。就像我说的,它工作了一段时间,所以我不认为它有什么大不了的。 如错误所示,您的数组之一为空。检查数组中的数据。 @Pankaj 是的,我知道,[self.fetchedResultsController sections][section] 数组是空的,但它不应该是因为self.fetchedResultsController's fetchedObjects 返回 8 个对象。 崩溃发生时section的值是多少? 【参考方案1】:

您在评论中说fetchedObjects 返回 8 个对象。在numberOfSectionsInCollectionView 中,您使用它来计算集合视图中的部分数量:

NSInteger photoCount = [[self.fetchedResultsController fetchedObjects] count];
NSInteger sectionCount = (photoCount + 3) / 3;
return sectionCount;

假设计数确实是 8,这意味着 sectionCount 是 3(11 / 3,整数除法)。请注意,您不是在询问 fetched results controller 它有多少个部分,而是告诉集合视图它应该包含三个部分。

由于您在上面返回 3,collectionView:numberOfItemsInSection: 将被调用 3 次,其中部分值为 0、1 和 2。您将 section 值传递给获取的结果控制器:

id <NSFetchedResultsSectionInfo> sectionInfo = [self.fetchedResultsController sections][section];

这仅在获取的结果控制器实际上有 3 个部分时才有效。 但你从来没有问过它有多少部分,所以不能保证它有那么多部分。在这种情况下,极有可能发生崩溃。

看起来发生的事情是:

您在创建提取结果控制器时没有使用sectionNameKeyPath 因此[self.fetchedResultsController sections] 返回一个空数组 但是您尝试在该空数组中查找一个对象,因为您假设会有 3 个部分而不检查这是否正确。

因此你崩溃了。您不能只告诉 fetched results 控制器它应该有多少个部分。部分的数量基于提取结果,除非包含sectionNameKeyPath,否则没有任何部分。

【讨论】:

我编辑了我的帖子以显示我的 fetchedResultsController 的二传手。我正在使用sectionNameKeyPath,它可以容纳额外的单元格并将每张照片放在它所属的部分中。 我已将其标记为答案,因为它是正确的,但是,我在单独的答案中包含了为我解决问题的代码,以防其他人将来遇到此问题。 好的,你确实有一个sectionNameKeyPath,但你仍然不能假设获取的结果控制器至少有 3 个部分。【参考方案2】:

原来collectionView:numberOfItemsInSection: 是在NSFetchedResultsController 执行performFetch: 之前被调用的,因此[self.fetchedResultsController sections] 是空的。为了解决这个问题,我只是用一些简单的逻辑手动设置每个部分的项目数。

-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section

    NSArray *fetchedObjects = [self.fetchedResultsController fetchedObjects];
    NSInteger count = [fetchedObjects count] + 1;
    NSInteger sections = count / 3;
    if(sections > section) return 3;
    else return count % 3;

【讨论】:

以上是关于数据源方法collectionView:numberOfItemsInSection:崩溃应用程序“索引0超出空数组的范围”的主要内容,如果未能解决你的问题,请参考以下文章

数据源方法collectionView:numberOfItemsInSection:崩溃应用程序“索引0超出空数组的范围”

为啥我的collectionView中的单元格的形成速度比接收数据的方法快,因为它们的形成?

Swift - 使用核心数据和CollectionView进行分页

类方法不返回python中的排序列表

一页中的两个collectionView具有不同的数据

UICollectionView 重新加载数据无法更新 CollectionView