在ios应用程序上缓存图像的最佳方法?

Sti*_*Sti 49 caching load image uitableview ios

不久,我有一个NSDictionary我需要在我的图像中显示图像的网址UITableView.每个单元格都有标题和图像.我成功地实现了这一点,虽然滚动是滞后的,因为看起来细胞每次进入屏幕时都会下载图像.我搜索了一下,SDWebImage在github上找到了.这使得卷轴拉开了.我不完全确定它做了什么,但我相信它做了一些缓存.但!每当我第一次打开应用程序时,我看到没有图像,我必须向下滚动,然后备份它们才能到达.如果我用主页按钮退出应用程序并再次打开,那么它就像缓存一样工作,因为屏幕上的图像是可见的,但是,如果我向下滚动一个单元格,那么下一个单元格没有图像.直到我滚过它并备份,或者我点击它.这是缓存应该如何工作?或者缓存从网上下载的图像的最佳方法是什么?图像正在被更新,所以我接近将它们导入到项目中,但我希望能够在不上传更新的情况下更新图像.

是否无法在启动时从缓存中加载整个tableview的所有图像(假设缓存中有某些内容)?这就是为什么我有时会看到没有图像的细胞?

是的,我很难理解缓存是什么.

- 编辑 -

我只使用相同大小的图像(500x150)尝试了这个,并且方面错误消失了,但是当我向上或向下滚动时,所有单元格上都有图像,但起初它们是错误的.单元格在视图中显示几毫秒后,将显示正确的图像.这真令人讨厌,但也许它必须如何?它似乎首先从缓存中选择了错误的索引.如果我滚动慢,那么我可以看到图像从错误的图像闪烁到正确的图像.如果我快速滚动,那么我相信错误的图像在任何时候都是可见的,但由于快速滚动我无法分辨.当快速滚动减慢并最终停止时,仍会出现错误的图像,但在停止滚动后立即更新为正确的图像.我也有一个自定义UITableViewCell类,但我没有做过任何大的改动.我还没有完成我的代码,但我想不出可能出错的地方..也许我有错误的东西订单..我已经编写了很多java,c#,php等,但我很难理解Objective-c,所有的... .h.m我也有`

@interface FirstViewController : UITableViewController{

/**/
NSCache *_imageCache;
}
Run Code Online (Sandbox Code Playgroud)

(在其他变量中)FirstViewController.h.这不正确吗?

这是我的cellForRowAtIndexPath.

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

if (cell == nil)
{
    cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}

NSMutableArray *marr = [hallo objectAtIndex:indexPath.section];
NSDictionary *dict = [marr objectAtIndex:indexPath.row];

NSString* imageName = [dict objectForKey:@"Image"];
//NSLog(@"url: %@", imageURL);

UIImage *image = [_imageCache objectForKey:imageName];

if(image)
{
    cell.imageView.image = image;
}
else
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        NSString* imageURLString = [NSString stringWithFormat:@"example.com/%@", imageName];
        NSURL *imageURL = [NSURL URLWithString:imageURLString];
        UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:imageURL]];

        if(image)
        {
            dispatch_async(dispatch_get_main_queue(), ^{
                CustomCell *cell =(CustomCell*)[self.tableView cellForRowAtIndexPath:indexPath];
                if(cell)
                {
                    cell.imageView.image = image;
                }
            });
            [_imageCache setObject:image forKey:imageName];
        }
    });
}

cell.textLabel.text = [dict objectForKey:@"Name"];

return cell;
}
Run Code Online (Sandbox Code Playgroud)

Cal*_*leb 74

缓存只是意味着保留所需数据的副本,这样您就不必从较慢的源加载它.例如,微处理器通常具有高速缓存存储器,它们保存数据副本,这样它们就不必访问RAM,这要慢得多.硬盘通常具有内存缓存,文件系统可以从中快速访问最近访问过的数据块.

同样,如果您的应用从网络加载了大量图片,则可能需要将它们缓存在您的设备上而不是每次需要时都下载它们.有很多方法可以做到这一点 - 听起来你已经找到了一个.您可能希望将下载的图像存储在应用程序的/ Library/Caches目录中,特别是如果您不希望它们发生更改.从二级存储加载图像将比通过网络加载图像快得多.

您可能还对这个鲜为人知的NSCache类感兴趣,以便在内存中保留您需要的图像.NSCache就像一本字典,但是当内存变得紧张时,它会开始释放它的一些内容.您可以先检查给定图像的缓存,如果在那里找不到它,则可以查看缓存目录,如果没有找到它,则可以下载它.这些都不会在您第一次运行时加快应用程序上的图像加载速度,但是一旦您的应用程序下载了大部分需要的内容,它就会响应得更快.


Rob*_*Rob 23

我认为Caleb很好地回答了缓存问题.我刚刚在您检索图像时触及更新UI的过程,例如假设您有一个NSCache名为的图像_imageCache:

首先,为tableview定义一个操作队列属性:

@property (nonatomic, strong) NSOperationQueue *queue;
Run Code Online (Sandbox Code Playgroud)

然后viewDidLoad,初始化:

self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;
Run Code Online (Sandbox Code Playgroud)

然后cellForRowAtIndexPath,你可以:

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

    // set the various cell properties

    // now update the cell image

    NSString *imagename = [self imageFilename:indexPath]; // the name of the image being retrieved

    UIImage *image = [_imageCache objectForKey:imagename];

    if (image)
    {
        // if we have an cachedImage sitting in memory already, then use it

        cell.imageView.image = image;
    }
    else
    {
        cell.imageView.image = [UIImage imageNamed:@"blank_cell_image.png"];

        // the get the image in the background

        [self.queue addOperationWithBlock:^{

            // get the UIImage

            UIImage *image = [self getImage:imagename];

            // if we found it, then update UI

            if (image)
            {
                [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                    // if the cell is visible, then set the image

                    UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
                    if (cell)
                        cell.imageView.image = image;
                }];

                [_imageCache setObject:image forKey:imagename];
            }
        }];
    }

    return cell;
}
Run Code Online (Sandbox Code Playgroud)

我只提到了这一点,因为我最近在SO上看到了几个使用GCD更新相应UIImageView image属性的代码示例,但是在将UI更新发送回主队列的过程中,他们采用了好奇的技术(例如,重新加载)单元格或表格,只更新cell在顶部返回的现有对象的图像属性tableView:cellForRowAtIndexPath(如果行已经滚动离开屏幕并且单元格已经出列并且正在重新用于新行,则会出现问题),等等).通过使用cellForRowAtIndexPath(不要混淆tableView:cellForRowAtIndexPath),您可以确定单元格是否仍然可见和/或它是否已滚动并已出列并重新使用.


Mat*_*jda 13

最简单的解决方案是使用经过压力测试的大量使用的东西.

SDWebImage是一个功能强大的工具,帮助我解决类似的问题,可以很容易地安装与可可豆荚.在podfile中:

platform :ios, '6.1'
pod 'SDWebImage', '~>3.6'
Run Code Online (Sandbox Code Playgroud)

设置缓存:

SDImageCache *imageCache = [[SDImageCache alloc] initWithNamespace:@"myNamespace"];
[imageCache queryDiskCacheForKey:myCacheKey done:^(UIImage *image)
{
    // image is not nil if image was found
}];
Run Code Online (Sandbox Code Playgroud)

缓存图片:

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];
Run Code Online (Sandbox Code Playgroud)

https://github.com/rs/SDWebImage