数据源方法collectionView:numberOfItemsInSection:崩溃App"索引0超出空数组的边界"

时间:2014-05-05 04:02:49

标签: ios core-data uicollectionview nsfetchedresultscontroller

我整天都在处理这段代码,所以我可能会忽略一些愚蠢的东西,但我似乎无法找到问题,而且我的机智和#39;结束。

-(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部分)被替换为&#34;添加照片&#34;图像和所有照片indexPaths都经过调整以适应这种情况。它工作正常几个小时,但由于某些原因删除应用程序(重置核心数据)后,我似乎甚至无法加载视图。当它到达行* Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'时,它会立即崩溃,并显示错误id sectionInfo = [self.fetchedResultsController sections][section];。我不能为我的生活弄清楚。以下是我的其他数据源方法。

-(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 setter

- (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;
}

2 个答案:

答案 0 :(得分:2)

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

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

假设计数实际为8,则表示sectionCount为3(11 / 3,整数除法)。请注意,您不是要求获取的结果控制器它有多少部分,但是您告诉集合视图它应该有三个部分。

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

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

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

发生的事情是:

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

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

答案 1 :(得分:0)

事实证明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;
}