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

时间:2022-02-14 00:31:28

Shortly, I have an NSDictionary with urls for images that I need to show in my UITableView. Each cell has a title and an image. I had successfully made this happen, although the scrolling was lagging, as it seemed like the cells downloaded their image every time they came into the screen. I searched for a bit, and found SDWebImage on github. This made the scroll-lagg go away. I am not completely sure what it did, but I believed it did some caching. But! Every time I open the app for the first time, I see NO images, and I have to scroll down, and back up for them to arrive. And if I exit the app with home-button, and open again, then it seemes like the caching is working, because the images on the screen are visible, however, if I scroll one cell down, then the next cell has no image. Until i scroll past it and back up, or if I click on it. Is this how caching is supposed to work? Or what is the best way to cache images downloaded from the web? The images are being updated rarily, so I was close to just import them to the project, but I like to have the possibility to update images without uploading an update..

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

Is it impossible to load all the images for the whole tableview form the cache(given that there is something in the cache) at launch? Is that why I sometimes see cells without images?

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

And yes, I'm having a hard time understanding what cache is.

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

--EDIT--

- 编辑 -

I tried this with only images of the same size (500x150), and the aspect-error is gone, however when I scroll up or down, there are images on all cells, but at first they are wrong. After the cell has been in the view for some milliseconds, the right image appears. This is amazingly annoying, but maybe how it has to be?.. It seemes like it chooses the wrong index from the cache at first. If I scroll slow, then I can see the images blink from wrong image to the correct one. If I scroll fast, then I believe the wrong images are visible at all times, but I can't tell due to the fast scrolling. When the fast scrolling slows down and eventually stops, the wrong images still appear, but immediately after it stops scrolling, it updates to the right images. I also have a custom UITableViewCell class, but I haven't made any big changes.. I haven't gone through my code very much yet, but I can't think of what may be wrong.. Maybe I have something in the wrong order.. I have programmed much in java, c#, php etc, but I'm having a hard time understanding Objective-c, with all the .h and .m ... I have also `

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

@interface FirstViewController : UITableViewController{

/**/
NSCache *_imageCache;
}

(among other variables) in FirstViewController.h. Is this not correct?

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

Here's my cellForRowAtIndexPath.

这是我的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;
}

7 个解决方案

#1


61  

Caching just means keeping a copy of the data that you need so that you don't have to load it from some slower source. For example, microprocessors often have cache memory where they keep copies of data so that they don't have to access RAM, which is a lot slower. Hard disks often have memory caches from which the file system can get much quicker access to blocks of data that have been accessed recently.

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

Similarly, if your app loads a lot of images from the network, it may be in your interest to cache them on your device instead of downloading them every time you need them. There are lots of ways to do that -- it sounds like you already found one. You might want to store the images you download in your app's /Library/Caches directory, especially if you don't expect them to change. Loading the images from secondary storage will be much, much quicker than loading them over the network.

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

You might also be interested in the little-known NSCache class for keeping the images you need in memory. NSCache works like a dictionary, but when memory gets tight it'll start releasing some of its contents. You can check the cache for a given image first, and if you don't find it there you can then look in your caches directory, and if you don't find it there you can download it. None of this will speed up image loading on your app the first time you run it, but once your app has downloaded most of what it needs it'll be much more responsive.

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

#2


18  

I think Caleb answered the caching question well. I was just going to touch upon the process for updating your UI as you retrieve images, e.g. assuming you have a NSCache for your images called _imageCache:

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

First, define an operation queue property for the tableview:

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

@property (nonatomic, strong) NSOperationQueue *queue;

Then in viewDidLoad, initialize this:

然后在viewDidLoad中,初始化:

self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;

And then in cellForRowAtIndexPath, you could then:

然后在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;
}

I only mention this as I've seen a few code samples floating around on SO recently that use GCD to update the appropriate UIImageView image property, but in the process of dispatching the UI update back to the main queue, they employ curious techniques (e.g., reloading the cell or table, just updating the image property of the existing cell object returned at the top of the tableView:cellForRowAtIndexPath (which is a problem if the row has scrolled off the screen and the cell has been dequeued and is being reused for a new row), etc.). By using cellForRowAtIndexPath (not to be confused with tableView:cellForRowAtIndexPath), you can determine if the cell is still visible and/or if it may have scrolled off and been dequeued and reused.

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

#3


12  

The simplest solution is to go with something heavily used that has been stress tested.

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

SDWebImage is a powerful tool that helped me solve a similar problem and can easily be installed w/ cocoa pods. In podfile:

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

platform :ios, '6.1'
pod 'SDWebImage', '~>3.6'

Setup cache:

设置缓存:

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

Cache image:

缓存图片:

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

https://github.com/rs/SDWebImage

https://github.com/rs/SDWebImage

#4


1  

I think will be better for you user something like DLImageLoader. More info -> https://github.com/AndreyLunevich/DLImageLoader-iOS

我认为使用像DLImageLoader这样的用户会更好。更多信息 - > https://github.com/AndreyLunevich/DLImageLoader-iOS

[[DLImageLoader sharedInstance] loadImageFromUrl:@"image_url_here"
                                   completed:^(NSError *error, UIImage *image) {
                                        if (error == nil) {
                                            imageView.image = image;
                                        } else {
                                            // if we got an error when load an image
                                        }
                                   }];

#5


1  

For the part of the question about wrong images, it's because of the reuse of cells. Reuse of cells means that the existing cells, which go out of view (for example, the cells which go out of the screen in the top when you scroll towards the bottom are the ones coming back again from the bottom.) And so you get incorrect images. But once the cell shows up, the code for fetching the proper image executes and you get the proper images.

关于错误图像的部分问题,这是因为细胞的重复使用。重复使用单元格意味着现有单元格不在视野范围内(例如,当您向下滚动时,从顶部出来的单元格是从底部再次返回的单元格。)所以你得到了图片不正确。但是一旦单元格出现,就可以执行获取正确图像的代码,并获得正确的图像。

You can use a placeholder in 'prepareForReuse' method of the cell. This function is mostly used when you need to reset the values when the cell is brought up for reuse. Setting a placeholder here will make sure you won't get any incorrect images.

您可以在单元格的“prepareForReuse”方法中使用占位符。当您需要在启动单元以供重用时重置值时,通常会使用此功能。在此处设置占位符将确保您不会收到任何不正确的图像。

#6


0  

Caching images can be done as simply as this.

缓存图像可以像这样简单地完成。

ImageService.m

ImageService.m

@implementation ImageService{
    NSCache * Cache;
}

const NSString * imageCacheKeyPrefix = @"Image-";

-(id) init {
    self = [super init];
    if(self) {
        Cache = [[NSCache alloc] init];
    }
    return self;
}

/** 
 * Get Image from cache first and if not then get from server
 * 
 **/

- (void) getImage: (NSString *) key
        imagePath: (NSString *) imagePath
       completion: (void (^)(UIImage * image)) handler
    {
    UIImage * image = [Cache objectForKey: key];

    if( ! image || imagePath == nil || ! [imagePath length])
    {
        image = NOIMAGE;  // Macro (UIImage*) for no image 

        [Cache setObject:image forKey: key];

        dispatch_async(dispatch_get_main_queue(), ^(void){
            handler(image);
        });
    }
    else
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0ul ),^(void){

            UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[imagePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];

            if( !image)
            {
                image = NOIMAGE;
            }

            [Cache setObject:image forKey: key];

            dispatch_async(dispatch_get_main_queue(), ^(void){
                handler(image);
            });
        });
    }
}


- (void) getUserImage: (NSString *) userId
           completion: (void (^)(UIImage * image)) handler
{
    [self getImage: [NSString stringWithFormat: @"%@user-%@", imageCacheKeyPrefix, userId]
         imagePath: [NSString stringWithFormat: @"http://graph.facebook.com/%@/picture?type=square", userId]
        completion: handler];
}

SomeViewController.m

SomeViewController.m

    [imageService getUserImage: userId
                    completion: ^(UIImage *image) {
            annotationImage.image = image;
    }];

#7


0  

////.h file

#import <UIKit/UIKit.h>

@interface UIImageView (KJ_Imageview_WebCache)


-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image;

@end

//.m file


#import "UIImageView+KJ_Imageview_WebCache.h"


@implementation UIImageView (KJ_Imageview_WebCache)




-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image
{




    NSString *imageUrlString = urlString;
    NSURL *url = [NSURL URLWithString:urlString];



    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,     NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:[self tream_char:urlString]];

    NSLog(@"getImagePath--->%@",getImagePath);

    UIImage *customImage = [UIImage imageWithContentsOfFile:getImagePath];




    if (customImage)
    {
        self.image = customImage;


        return;
    }
    else
    {
         self.image=placeholder_image;
    }


    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *uploadTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {


        if (error)
        {
            NSLog(@"%@",[error localizedDescription]);

           self.image=placeholder_image;

            return ;
        }

        dispatch_async(dispatch_get_main_queue(), ^{


            UIImage *imageToCache = [UIImage imageWithData:data];



            if (imageUrlString == urlString)
            {

                self.image = imageToCache;
            }



            [self saveImage:data ImageString:[self tream_char:urlString]];

        });




    }];

    [uploadTask resume];



}


-(NSString *)tream_char :(NSString *)string
{
    NSString *unfilteredString =string;

    NSCharacterSet *notAllowedChars = [[NSCharacterSet characterSetWithCharactersInString:@"!@#$%^&*()_+|abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"] invertedSet];
    NSString *resultString = [[unfilteredString componentsSeparatedByCharactersInSet:notAllowedChars] componentsJoinedByString:@""];
    NSLog (@"Result: %@", resultString);



    return resultString;

}

-(void)saveImage : (NSData *)Imagedata ImageString : (NSString *)imageString
{

    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:imageString];




    if (![Imagedata writeToFile:documentDirectoryFilename atomically:NO])
    {
        NSLog((@"Failed to cache image data to disk"));
    }
    else
    {
        NSLog(@"the cachedImagedPath is %@",documentDirectoryFilename);
    }

}

@end


/// call 

 [cell.ProductImage loadImageUsingUrlString:[[ArrProductList objectAtIndex:indexPath.row] valueForKey:@"product_image"] placeholder:[UIImage imageNamed:@"app_placeholder"]];

#1


61  

Caching just means keeping a copy of the data that you need so that you don't have to load it from some slower source. For example, microprocessors often have cache memory where they keep copies of data so that they don't have to access RAM, which is a lot slower. Hard disks often have memory caches from which the file system can get much quicker access to blocks of data that have been accessed recently.

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

Similarly, if your app loads a lot of images from the network, it may be in your interest to cache them on your device instead of downloading them every time you need them. There are lots of ways to do that -- it sounds like you already found one. You might want to store the images you download in your app's /Library/Caches directory, especially if you don't expect them to change. Loading the images from secondary storage will be much, much quicker than loading them over the network.

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

You might also be interested in the little-known NSCache class for keeping the images you need in memory. NSCache works like a dictionary, but when memory gets tight it'll start releasing some of its contents. You can check the cache for a given image first, and if you don't find it there you can then look in your caches directory, and if you don't find it there you can download it. None of this will speed up image loading on your app the first time you run it, but once your app has downloaded most of what it needs it'll be much more responsive.

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

#2


18  

I think Caleb answered the caching question well. I was just going to touch upon the process for updating your UI as you retrieve images, e.g. assuming you have a NSCache for your images called _imageCache:

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

First, define an operation queue property for the tableview:

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

@property (nonatomic, strong) NSOperationQueue *queue;

Then in viewDidLoad, initialize this:

然后在viewDidLoad中,初始化:

self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 4;

And then in cellForRowAtIndexPath, you could then:

然后在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;
}

I only mention this as I've seen a few code samples floating around on SO recently that use GCD to update the appropriate UIImageView image property, but in the process of dispatching the UI update back to the main queue, they employ curious techniques (e.g., reloading the cell or table, just updating the image property of the existing cell object returned at the top of the tableView:cellForRowAtIndexPath (which is a problem if the row has scrolled off the screen and the cell has been dequeued and is being reused for a new row), etc.). By using cellForRowAtIndexPath (not to be confused with tableView:cellForRowAtIndexPath), you can determine if the cell is still visible and/or if it may have scrolled off and been dequeued and reused.

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

#3


12  

The simplest solution is to go with something heavily used that has been stress tested.

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

SDWebImage is a powerful tool that helped me solve a similar problem and can easily be installed w/ cocoa pods. In podfile:

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

platform :ios, '6.1'
pod 'SDWebImage', '~>3.6'

Setup cache:

设置缓存:

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

Cache image:

缓存图片:

[[SDImageCache sharedImageCache] storeImage:myImage forKey:myCacheKey];

https://github.com/rs/SDWebImage

https://github.com/rs/SDWebImage

#4


1  

I think will be better for you user something like DLImageLoader. More info -> https://github.com/AndreyLunevich/DLImageLoader-iOS

我认为使用像DLImageLoader这样的用户会更好。更多信息 - > https://github.com/AndreyLunevich/DLImageLoader-iOS

[[DLImageLoader sharedInstance] loadImageFromUrl:@"image_url_here"
                                   completed:^(NSError *error, UIImage *image) {
                                        if (error == nil) {
                                            imageView.image = image;
                                        } else {
                                            // if we got an error when load an image
                                        }
                                   }];

#5


1  

For the part of the question about wrong images, it's because of the reuse of cells. Reuse of cells means that the existing cells, which go out of view (for example, the cells which go out of the screen in the top when you scroll towards the bottom are the ones coming back again from the bottom.) And so you get incorrect images. But once the cell shows up, the code for fetching the proper image executes and you get the proper images.

关于错误图像的部分问题,这是因为细胞的重复使用。重复使用单元格意味着现有单元格不在视野范围内(例如,当您向下滚动时,从顶部出来的单元格是从底部再次返回的单元格。)所以你得到了图片不正确。但是一旦单元格出现,就可以执行获取正确图像的代码,并获得正确的图像。

You can use a placeholder in 'prepareForReuse' method of the cell. This function is mostly used when you need to reset the values when the cell is brought up for reuse. Setting a placeholder here will make sure you won't get any incorrect images.

您可以在单元格的“prepareForReuse”方法中使用占位符。当您需要在启动单元以供重用时重置值时,通常会使用此功能。在此处设置占位符将确保您不会收到任何不正确的图像。

#6


0  

Caching images can be done as simply as this.

缓存图像可以像这样简单地完成。

ImageService.m

ImageService.m

@implementation ImageService{
    NSCache * Cache;
}

const NSString * imageCacheKeyPrefix = @"Image-";

-(id) init {
    self = [super init];
    if(self) {
        Cache = [[NSCache alloc] init];
    }
    return self;
}

/** 
 * Get Image from cache first and if not then get from server
 * 
 **/

- (void) getImage: (NSString *) key
        imagePath: (NSString *) imagePath
       completion: (void (^)(UIImage * image)) handler
    {
    UIImage * image = [Cache objectForKey: key];

    if( ! image || imagePath == nil || ! [imagePath length])
    {
        image = NOIMAGE;  // Macro (UIImage*) for no image 

        [Cache setObject:image forKey: key];

        dispatch_async(dispatch_get_main_queue(), ^(void){
            handler(image);
        });
    }
    else
    {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0ul ),^(void){

            UIImage * image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[imagePath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]]];

            if( !image)
            {
                image = NOIMAGE;
            }

            [Cache setObject:image forKey: key];

            dispatch_async(dispatch_get_main_queue(), ^(void){
                handler(image);
            });
        });
    }
}


- (void) getUserImage: (NSString *) userId
           completion: (void (^)(UIImage * image)) handler
{
    [self getImage: [NSString stringWithFormat: @"%@user-%@", imageCacheKeyPrefix, userId]
         imagePath: [NSString stringWithFormat: @"http://graph.facebook.com/%@/picture?type=square", userId]
        completion: handler];
}

SomeViewController.m

SomeViewController.m

    [imageService getUserImage: userId
                    completion: ^(UIImage *image) {
            annotationImage.image = image;
    }];

#7


0  

////.h file

#import <UIKit/UIKit.h>

@interface UIImageView (KJ_Imageview_WebCache)


-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image;

@end

//.m file


#import "UIImageView+KJ_Imageview_WebCache.h"


@implementation UIImageView (KJ_Imageview_WebCache)




-(void)loadImageUsingUrlString :(NSString *)urlString placeholder :(UIImage *)placeholder_image
{




    NSString *imageUrlString = urlString;
    NSURL *url = [NSURL URLWithString:urlString];



    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,     NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *getImagePath = [documentsDirectory stringByAppendingPathComponent:[self tream_char:urlString]];

    NSLog(@"getImagePath--->%@",getImagePath);

    UIImage *customImage = [UIImage imageWithContentsOfFile:getImagePath];




    if (customImage)
    {
        self.image = customImage;


        return;
    }
    else
    {
         self.image=placeholder_image;
    }


    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *uploadTask = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {


        if (error)
        {
            NSLog(@"%@",[error localizedDescription]);

           self.image=placeholder_image;

            return ;
        }

        dispatch_async(dispatch_get_main_queue(), ^{


            UIImage *imageToCache = [UIImage imageWithData:data];



            if (imageUrlString == urlString)
            {

                self.image = imageToCache;
            }



            [self saveImage:data ImageString:[self tream_char:urlString]];

        });




    }];

    [uploadTask resume];



}


-(NSString *)tream_char :(NSString *)string
{
    NSString *unfilteredString =string;

    NSCharacterSet *notAllowedChars = [[NSCharacterSet characterSetWithCharactersInString:@"!@#$%^&*()_+|abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"] invertedSet];
    NSString *resultString = [[unfilteredString componentsSeparatedByCharactersInSet:notAllowedChars] componentsJoinedByString:@""];
    NSLog (@"Result: %@", resultString);



    return resultString;

}

-(void)saveImage : (NSData *)Imagedata ImageString : (NSString *)imageString
{

    NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);

    NSString* documentDirectory = [documentDirectories objectAtIndex:0];
    NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:imageString];




    if (![Imagedata writeToFile:documentDirectoryFilename atomically:NO])
    {
        NSLog((@"Failed to cache image data to disk"));
    }
    else
    {
        NSLog(@"the cachedImagedPath is %@",documentDirectoryFilename);
    }

}

@end


/// call 

 [cell.ProductImage loadImageUsingUrlString:[[ArrProductList objectAtIndex:indexPath.row] valueForKey:@"product_image"] placeholder:[UIImage imageNamed:@"app_placeholder"]];