由于 NSRangeException (NSMutableArray) 导致应用程序终止

Posted

技术标签:

【中文标题】由于 NSRangeException (NSMutableArray) 导致应用程序终止【英文标题】:Application Termination because of NSRangeException (NSMutableArray) 【发布时间】:2015-08-20 20:58:44 【问题描述】:

我正在尝试从 Parse 中检索上传的 thumbnailPhotos 以在 UITableViewCells 中显示它们,但每次都会抛出异常。错误代码如下:“Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'” 这是我的代码:

- (void) viewWillAppear:(BOOL)animated 
[super viewWillAppear:animated];

PFQuery *query = [PFQuery queryWithClassName:@"Events"];
[query orderByDescending:@"date"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) 
    if (error) 
        NSLog(@"Error: %@ %@", error, [error userInfo]);
     else 
        self.events = (NSMutableArray *) objects;
        [self.tableView reloadData];

        for (PFObject *event in self.events) 

            NSInteger index = [self.events indexOfObject:event];

            PFFile *imageFile = [event objectForKey:@"thumbnailImage"];

            [imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) 

                if (error) 
                    //Handle Error
                 else 

                    UIImage *image = [UIImage imageWithData:result];

                    if (self.thumbnailPhotos == nil) 

                        self.thumbnailPhotos = [NSMutableArray array];
                        self.thumbnailPhotos[index] = image;
                     else 

                    self.thumbnailPhotos[index] = image;
                    

                    [self.tableView reloadData];
                

            ];
        
    
];

CellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
static NSString *reuseIdentifier = @"Cell";
EventsTableViewCell *cell = (EventsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];

PFObject *event = [self.events objectAtIndex:indexPath.row];

NSDate *date = [event objectForKey:@"date"];

NSString *dateString = [self.dateFormat stringFromDate:date];
NSString *timeString = [self.timeFormat stringFromDate:date];
NSLog(@"IndexPath.row = %ld", (long)indexPath.row);

if ([self.thumbnailPhotos objectAtIndex:indexPath.row] != nil) 

    cell.imageView.image = self.thumbnailPhotos[indexPath.row];
 else 
    NSLog(@"Nil, Application will crash!");


cell.eventNameLabel.text = [event objectForKey:@"title"];
cell.dateLabel.text = dateString;
cell.timeLabel.text = timeString;
[cell.timeLabel sizeToFit];

return cell;

`

我不得不添加 self.events 的索引值,因为 thumbnailPhotos 以不同的速度下载,所以我的 Cells 总是为错误的事件显示错误的照片。 我希望这些细节足以解决问题。

【问题讨论】:

崩溃发生在self.thumbnailPhotos[index] = image;线上吗? 根本原因是重新加载数据必须在所有图像提取发生之后(在您当前的设计下)发生。按照它的编码方式,重新加载发生在 任何 完成之前。解决方案是(a)等待重新加载,直到完成图像数据列表的提取,或者(b)在配置单元格时延迟加载图像。我可以告诉你怎么做(很多其他人也可以)。由你决定。 @MikeAtNobel,问题应该已经说明了这一点,但我 99% 确定失败的行是if ([self.thumbnailPhotos objectAtIndex:indexPath.row] != nil) 。请参阅我之前的评论以了解原因。 是的,它在 danh 所述的行中。我实际上并不擅长编码,但我正在练习并努力变得更好,所以如果你能告诉我如何实现这两种解决方案,或者建议我更好的方法来编写这个功能,那就太好了。 【参考方案1】:

应用程序崩溃,因为 thumbnailPhotos 在索引中没有任何对象用于分配。请使用以下代码。

更新的代码将支持字典保存缩略图图像

 /*

     Define events as NSMutableArray which holds all events 
     Define thumbnailPhotos as NSMutableDictionary which holds thumbnail image for index as key

     */

        //Create a weak Reference of self for accessing self within block

        __weak __typeof(self)weakSelf = self;
        PFQuery *query = [PFQuery queryWithClassName:@"Events"];
        [query orderByDescending:@"date"];
        [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) 
            if (error) 
                NSLog(@"Error: %@ %@", error, [error userInfo]);
             else 
                //Create a strong reference for updating the UI
                __strong __typeof(weakSelf)strongSelf = weakSelf;

                //Assign the events to instance object
                strongSelf.events = [NSMutableArray arrayWithArray:objects];

                //Alloced thumbnail dictionary
                strongSelf.thumbnailPhotos = [NSMutableDictionary dictionary];

                //Reload tableView so that data will be visible
                [strongSelf.tableView reloadData];

                for (PFObject *event in strongSelf.events) 

                    //Define index as block type because we have to use this instance within block
                   __block NSInteger index = [strongSelf.events indexOfObject:event];

                    PFFile *imageFile = [event objectForKey:@"thumbnailImage"];
                    [imageFile getDataInBackgroundWithBlock:^(NSData *result, NSError *error) 

                        if (error) 
                            //Handle Error
                         else 

                            UIImage *image = [UIImage imageWithData:result];

                            //Set the image against index
                            [strongSelf.thumbnailPhotos setObject:@"" forKey:@(index)];

                            //Reload only cell for which image is just downloaded
                            [strongSelf.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:index inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
                        

                    ];
                
            
        ];

更新: 如下修改你的 cellForRowAtIndexPath

 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
    static NSString *reuseIdentifier = @"Cell";
    EventsTableViewCell *cell = (EventsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:reuseIdentifier forIndexPath:indexPath];

    PFObject *event = [self.events objectAtIndex:indexPath.row];

    NSDate *date = [event objectForKey:@"date"];

    NSString *dateString = [self.dateFormat stringFromDate:date];
    NSString *timeString = [self.timeFormat stringFromDate:date];
    NSLog(@"IndexPath.row = %ld", (long)indexPath.row);

    if ([self.thumbnailPhotos valueForKey:@(indexPath.row)]) 

        cell.imageView.image = [self.thumbnailPhotos valueForKey:@(indexPath.row)];
     else 
        NSLog(@"Nil, Application will crash!");
    

    cell.eventNameLabel.text = [event objectForKey:@"title"];
    cell.dateLabel.text = dateString;
    cell.timeLabel.text = timeString;
    [cell.timeLabel sizeToFit];

    return cell;

在这个实现中,缩略图图像被保存到字典中,并且 tableView 的第二次重新加载是基于每个单元格的,而不是重新加载完整的 tableView 以下载单个缩略图。

【讨论】:

这个会在测试时崩溃以避免崩溃。 到目前为止,这个实际上运行良好,谢谢。 :) 所以我的代码唯一的问题是我没有将“占位符”对象添加到我的缩略图数组中?我想知道我做错了什么,这样我可能会更多地了解编码。 @danh 我修改了源代码有更稳定的解决方案。请试一试 @Strikecounter2 您没有将占位符图像添加到数组中并尝试从数组中访问对象,但是您的数组根本没有任何对象。 但我不太明白为什么它仍然试图访问数组中丢失的对象。我检查了给定的 indexPath.row 是否有对象,所以如果没有对象,什么都不会发生。

以上是关于由于 NSRangeException (NSMutableArray) 导致应用程序终止的主要内容,如果未能解决你的问题,请参考以下文章

由于未捕获的异常“NSRangeException”而终止应用程序,原因:“*** -[__NSArrayM objectAtIndex:]:索引 0 超出空数组的范围”

由于未捕获的异常“NSRangeException”而终止应用程序,原因:-[__NSArrayM objectAtIndex:]:索引 0 超出空数组的范围

由于未捕获的异常“NSRangeException”而终止应用程序,原因:“-[__NSCFArray objectAtIndex:]: index (14) beyond bounds (14)”

*** 由于未捕获的异常“NSRangeException”而终止应用程序,原因:“*** -[__NSArrayI objectAtIndex:]: index 3 beyond bounds [0

由于未捕获的异常“NSRangeException”而终止应用程序,原因:-[__NSArrayM objectAtIndex:]:索引 0 超出空数组的范围-2

*** 由于未捕获的异常“NSRangeException”而终止应用程序,原因:“*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds fo