最好的方法缓存在iOS应用的图像?(Best way to cache images on ios

2019-06-17 10:50发布

不久,我有一个NSDictionary与网址,我需要在我的显示图像UITableView 。 每个单元都有一个标题和图像。 我已经成功地让这一切发生,虽然滚动被滞后,因为它似乎像每一个细胞都带入画面时下载自己的形象。 我搜索了一下,发现SDWebImage在GitHub上。 这使滚动LAGG消失。 我不能完全肯定它的所作所为,但我相信它做了一些缓存。 但! 每次我打开的第一次应用,我没有看到的图像,我需要向下滚动,并且备份他们的到来。 如果我再次退出与家庭按钮,并打开该应用,然后将其seemes像缓存不过工作,因为屏幕上的图像是可见的,如果我滚动一个单元格下来,然后在下一个单元没有图像。 直到我滚过,并备份,或者如果我点击它。 这是缓存如何工作的? 或什么是缓存从网上下载的图片的最佳方式? 这些图像被rarily更新,所以我很接近,只是它们导入到项目,但我喜欢有没有上传更新来更新图像的可能性..

它是不可能加载所有整个实现代码如下形式缓存中的图像(因为没有在高速缓存中的东西)的推出? 这就是为什么我有时会看到细胞无图像?

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

- 编辑 -

但是,我想这与同尺寸(500x150)的仅适用于图像,纵横错走了,当我向上或向下滚动,那里所有的细胞图像,但一开始他们都错了。 细胞已经在几毫秒的观点后,出现右侧图像。 这是令人惊讶的烦人,但也许它如何是?..它seemes喜欢选择从缓存中错误的索引在第一。 如果我滚动慢,然后我可以看到图片来自错误图像闪烁,正确的。 如果我滚动快的话,我相信了错误的图像在任何时候都是可见的,但我不能因快速滚动告诉。 当快速滚动减慢并最终停止,错误的图像仍然会出现,但之后它停止滚动,它更新到正确的图像。 我也有一个自定义UITableViewCell类,但我还没有做出任何大的改动。我没有经过我的代码了很多,但我想不出什么可能是错的..也许我在东西错误的命令。我已经设定用Java,C#,PHP等不多,但我有一个很难理解的Objective-C,与所有.h.m ......我也有`

@interface FirstViewController : UITableViewController{

/**/
NSCache *_imageCache;
}

(除其他变量)中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;
}

Answer 1:

缓存只是意味着保持你需要,这样你就不必从一些慢源加载数据的副本。 例如,微处理器通常具有高速缓存存储器,他们保留数据的副本,这样他们就不必访问RAM,这是慢了很多。 硬盘常常有内存缓存从该文件系统可以获取到最近访问过的数据块更快的访问。

同样,如果你的应用程序加载大量来自网络的图像,它可能是你的兴趣对其进行缓存设备上,而不是每次你需要的时候下载它们。 有很多方法可以做到这一点 - 这听起来像你已经找到了一个。 您可能希望存储在您的应用程序的/图书馆/缓存目录中下载图像,特别是如果你不希望他们改变。 从加载二级存储的图像将是多少,比加载它们在网络上更快。

您可能也有兴趣鲜为人知NSCache类让你在内存中所需要的图像。 NSCache就像一本字典,但是当内存变得紧,它会开始释放它的一些内容。 你可以先检查一个给定的图像缓存,如果你不觉得它那里,你可以再看看你的缓存目录,如果你没有找到它,你可以下载它。 这一切都不会加快图像加载在你的应用程序,你运行它的第一次,但一旦你的应用程序已经下载的大多数的它需要它会更为敏感。



Answer 2:

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:

First, define an operation queue property for the tableview:

@property (nonatomic, strong) NSOperationQueue *queue;

Then in viewDidLoad, initialize this:

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

And then in cellForRowAtIndexPath, you could then:

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



Answer 3:

最简单的办法是去的东西大量使用已经压力测试。

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

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

设置缓存:

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

图像高速缓存:

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

https://github.com/rs/SDWebImage



Answer 4:

我想会为你的用户像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
                                        }
                                   }];


Answer 5:

有关错误图像问题的一部分,这是因为电池的再利用。 电池的再利用是指现有细胞,走出去的视图(例如,它走出去在屏幕上方,当你向底部滚动细胞是那些从底部再回来的。)等你拿不正确的图像。 但是,一旦细胞显示出来,包括获取适当的图像的代码执行,你会得到正确的图像。

您可以在单元格中的“prepareForReuse”的方法使用的占位符。 当你需要时,电池拿出来再使用重置值时,此功能主要是使用。 在这里设置一个占位符,将确保你不会得到任何不正确的图像。



Answer 6:

缓存图像可以简单地为这件事。

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

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


Answer 7:

////.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"]];


文章来源: Best way to cache images on ios app?