简体   繁体   English

异步加载图像,奇怪的问题

[英]Loading images asynchronously, weird problems

There is my question: 我的问题是:

i have custom class that parse through an XML and get string i need to use as URL for my strings, now i modified my code as follow: 我有通过XML解析并获取字符串的自定义类,我需要将其用作字符串的URL,现在我将代码修改如下:

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

UILabel *labelText = (UILabel *)[cell viewWithTag:1000];
    labelText.text = [[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:@"name"];




    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(concurrentQueue, ^{
        NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:@"imageCell"]];

        NSData *image = [[NSData alloc] initWithContentsOfURL:imageURL];

        dispatch_async(dispatch_get_main_queue(), ^{



            cell.imageView.image = [UIImage imageWithData:image];
        });
    });

 return cell;
}

This is pretty straightforward, but, i got unpredictable errors! 这非常简单,但是,我遇到了无法预测的错误! During scrolling table, images start to chaotically change, sometimes it show 3 or more images and final image is correct one, sometimes final (correct) image does not appear at all. 在滚动表期间,图像开始混乱变化,有时显示3张或更多图像,而最终图像是正确的图像,有时最终(正确)图像根本不出现。 Also, when table is first shown, its actually blank, so i need to scroll it bottom, and then up again to see my images! 另外,当表格首次显示时,它实际上是空白的,因此我需要将其底部滚动,然后再次向上滚动以查看我的图像!

In attempt to fix that, i add following code, to determine is my image link correct for that indexPath: 为了解决这个问题,我添加了以下代码,以确定该indexPath的图像链接是否正确:

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    NSURL *imageURL = [NSURL URLWithString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row]objectForKey:@"imageCell"]];
    NSLog(@"%@", imageURL);
}

And when i tap to any cell, it does show me proper link in console log, but image on cell is one of the image shown before (invalid), and it is not the image for that link. 当我点击任何一个单元格时,它确实会向我显示控制台日志中的正确链接,但是单元格上的图像是之前显示的图像之一(无效),而不是该链接的图像。 How to fix that weird errors? 如何解决这些奇怪的错误?

Any advice would be appreciated, thank you. 任何建议,将不胜感激,谢谢。

When you dequeue a cell object, most of the time you'll get a reused cell ie a cell that has been configured by tableView:cellForRowAtIndexPath: once or more times before. 当您使一个单元格对象出队时 ,大多数时候您会得到一个重用的单元格,即一个由tableView:cellForRowAtIndexPath:之前配置过一次或多次的单元格。

To visualise what's happening in your case, consider one likely sequence of events for a single cell as you perform a long, quick scroll: 为了可视化您的情况,在执行长而快速的滚动时,请考虑单个单元格可能发生的一系列事件:

  1. The cell is created and an image load is spun off in the background 创建单元并在后台拆分图像加载
  2. The cell is scrolled off screen, so added to the table view's cache, ready for dequeuing. 单元格在屏幕外滚动,因此已添加到表视图的缓存中,准备出队。 The image loading is not canceled at this point 此时图像加载尚未取消
  3. The cell is dequeued and an image load is spun off in the background 单元出队并在后台分离图像加载
  4. Steps 2 and 3 are repeated a few times 步骤2和3重复几次
  5. The cell is visible, but the several image loading tasks are now completing and each is updating the cell's imageView with the loaded image. 该单元格是可见的,但是几个图像加载任务现在已完成,并且每个任务都使用加载的图像更新该单元格的imageView This will indeed look like the images are chaotically changing as each loading operation finishes. 确实看起来像图像在每次加载操作完成时都在混乱地变化。

(What's more, with a concurrent queue, there's no guarantee that the image loads will complete in the order that they're started - you may not end up with the correct final image!) (此外,对于并发队列,不能保证映像加载将按照启动顺序完成-您可能无法获得正确的最终映像!)

So what do we do about it? 那么我们该怎么办? Now that we understand the problem, there are lots of different solutions. 现在我们了解了问题所在,有很多不同的解决方案。 A very simple solution (that I don't really recommend) is to check that the cell's label text matches the value for that indexPath, when you come to set the image: 一个非常简单的解决方案(我并不真正推荐)是在设置图像时检查单元格的标签文本是否与该indexPath的值匹配:

if ([labelText.text isEqualToString:[[self.listOfPlaceDetails objectAtIndex:indexPath.row] objectForKey:@"name"]]) {
   cell.imageView.image = [UIImage imageWithData:image];
}

Obviously, this assumes that all the place details have unique names. 显然,这假设所有地点详细信息都具有唯一的名称。

A better solution might be to create an object that handles the image download, and is something that you can register/unregister cells against to handle download completion. 更好的解决方案可能是创建一个处理图像下载的对象,您可以针对该对象注册/注销单元格以处理下载完成。 This object could enforce the condition that a cell cannot be waiting for more than one image load. 该对象可以强制执行以下条件:单元不能等待一个以上的图像加载。 As @Leena pointed out, caching is a good idea and this object could be responsible for that too. 正如@Leena指出的那样,缓存是个好主意,该对象也可能对此负责。

As for the blank images, calling [cell setNeedsLayout] after setting the image should sort that out. 至于空白图像,设置图像后调用[cell setNeedsLayout]应该可以解决该问题。

Default property "imageView" will not be added to the cell until its (imageView's) property "image" is nil (you can check cell.imageView.superview will be nil too). 在其(imageView的)属性“ image”为零之前,默认属性“ imageView”将不会添加到单元格中(您可以检查cell.imageView.superview也将为零)。 That's why your tableView is blank when it is loaded and all images for cells are also loaded. 这就是为什么您的tableView加载时为空白,并且单元的所有图像也都加载的原因。 So when you scroll it down (or up) cells will be reloaded, their "imageView" will have image data. 因此,当您向下(或向上)滚动时,将重新加载单元格,其“ imageView”将具有图像数据。 That is the reason why they are on cell and you can see them. 这就是为什么它们在牢房中并且您可以看到它们的原因。

The other problem is that your images are flashing all the time. 另一个问题是图像一直在闪烁。 It happens because your cells are dequeued. 这是因为您的单元已出队。 So, when the image for the first cell is downloaded and setted it will not be shown until you relayout cell's subviews (for example by calling [cell setNeedsLayout];-). 因此,当下载并设置第一个单元格的图像时,直到您中继出单元格的子视图(例如,通过调用[cell setNeedsLayout] ;-),该图像才会显示。 And when you scroll table down, your first cell (with an image now) will be dequeued from "tableView" and will become last cell, and then your first cell will be shown with image that actually belongs to the first row. 当您向下滚动表格时,您的第一个单元格(现在带有图像)将从“ tableView”中出队,并成为最后一个单元格,然后您的第一个单元格将显示实际属于第一行的图像。 At the same time you start downloading image for this (last) row and after it was downloaded you will set it. 同时,您开始下载该(最后)行的图像,下载后将对其进行设置。 And this is the monent when flashing happens. 这是发生闪烁时的提示。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM