缓存URL图片iPhone UITableview

11

不必自己实现所有功能,你可以使用像UIImageLoader(https://github.com/gngrwzrd/UIImageLoader)或SDWebImage这样的帮助程序。UIImageLoader只有两个文件,非常简单易用。仓库中还有一个很好的示例。 - gngrwzrd
5个回答

35

这里是使用NSCache实现的简单ImageCache,它是一个单例。

ImageCache.h

    #import <Foundation/Foundation.h>

    @interface ImageCache : NSObject

    @property (nonatomic, retain) NSCache *imgCache;


    #pragma mark - Methods

    + (ImageCache*)sharedImageCache;
    //- (void) AddImage:(NSString *)imageURL: (UIImage *)image;
   - (void) AddImage:(NSString *)imageURL withImage:(UIImage *)image;
    - (UIImage*) GetImage:(NSString *)imageURL;
    - (BOOL) DoesExist:(NSString *)imageURL;

    @end

ImageCache.m

  #import "ImageCache.h"

    @implementation ImageCache

    @synthesize imgCache;

    #pragma mark - Methods

    static ImageCache* sharedImageCache = nil;

    +(ImageCache*)sharedImageCache
    {
        @synchronized([ImageCache class])
        {
            if (!sharedImageCache)
                sharedImageCache= [[self alloc] init];

            return sharedImageCache;
        }

        return nil;
    }

    +(id)alloc
    {
        @synchronized([ImageCache class])
        {
            NSAssert(sharedImageCache == nil, @"Attempted to allocate a second instance of a singleton.");
            sharedImageCache = [super alloc];

            return sharedImageCache;
        }

        return nil;
    }

    -(id)init 
    {
        self = [super init];
        if (self != nil) 
        {
            imgCache = [[NSCache alloc] init];
        }

        return self;
    }

   // - (void) AddImage:(NSString *)imageURL: (UIImage *)image
- (void) AddImage:(NSString *)imageURL withImage:(UIImage *)image
    {
        [imgCache setObject:image forKey:imageURL];
    }

    - (NSString*) GetImage:(NSString *)imageURL
    {
        return [imgCache objectForKey:imageURL];
    }

    - (BOOL) DoesExist:(NSString *)imageURL
    {
        if ([imgCache objectForKey:imageURL] == nil)
        {
            return false;
        }

        return true;
    }


    @end

例子

UIImage *image;

    // 1. Check the image cache to see if the image already exists. If so, then use it. If not, then download it.

    if ([[ImageCache sharedImageCache] DoesExist:imgUrl] == true)
    {
        image = [[ImageCache sharedImageCache] GetImage:imgUrl];
    }
    else
    {
        NSData *imageData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: imgUrl]];
        image = [[UIImage alloc] initWithData:imageData];

        // Add the image to the cache 
        //[[ImageCache sharedImageCache] AddImage:imgUrl :image];

        [[ImageCache sharedImageCache] AddImage:imgUrl withImage:image];
    }

1
感谢您的出色回答。它充满了所需的信息。 - Umut Koseali
非常有帮助的答案 +1,我有一个问题,这些缓存图像存储在哪里?我已经检查了文档目录,但没有找到它,另一个问题是这段代码如何删除这些缓存图像或者是否不需要这样做? - Dilip Manek
1
干得好。但我建议1. 遵循camelCase命名约定来命名你的方法; 2. 使用dispatch_once来实例化你的单例(参见Matt Galloway的单例讨论)。 - Rob
你好, 我做了和你这里一样的事情,但是它没有起作用。我想在customCell中使用NSCache显示缩略图像。这是我的帖子, http://stackoverflow.com/questions/19252991/nsinvalidargumentexception-reason-nscache-setobjectforkeycost-attempt?noredirect=1#comment28501679_19252991 请看一下。谢谢。 - Tulon
不错。但是一旦缓存中有很多图像,就会引发内存警告,并最终可能导致崩溃。为什么不在init中添加memoryWarning: 的观察者,并在该方法被触发时删除缓存对象呢? - Gautam Jain
显示剩余2条评论

8

4
iOS 5 的更新版本: git clone http://ezekiel.vancouver.wsu.edu/~cs458/demos/YellowJackets/.git 链接 - wcochran

2
您可以尝试使用由enormego的聪明家伙编写的优秀的EgoImage库来实现此目的。它非常简单易用,能够在后台高效地使用缓存,并且非常适合满足您的要求。
这是包含演示应用程序的库的github路径

0

我写了这个(其中的概念和一些代码来自于Lane Roathe的优秀UIImageView+Cache类别),用于我正在开发的一个应用程序。它也使用了ASIHTTPRequest类,这些类非常棒。这个程序肯定还有改进的空间...例如,如果不再需要,可以允许取消请求,或者利用通知userInfo来允许更精确的UI更新...但是对于我的目的来说,它已经很好地工作了。

@implementation ImageFetcher
#define MAX_CACHED_IMAGES 20
static NSMutableDictionary* cache = nil;

+ (void)asyncImageFetch:(UIImage**)anImagePtr withURL:(NSURL*)aUrl {

    if(!cache) {
        cache = [[NSMutableDictionary dictionaryWithCapacity:MAX_CACHED_IMAGES] retain];
    }

    UIImage* newImage = [cache objectForKey:aUrl.description];
    if(!newImage) { // cache miss - doh!
        ASIHTTPRequest *imageRequest = [ASIHTTPRequest requestWithURL:aUrl];    
        imageRequest.userInfo = [NSDictionary dictionaryWithObject:[NSValue valueWithPointer:anImagePtr] forKey:@"imagePtr"];
        imageRequest.delegate = self;
        [imageRequest setDidFinishSelector:@selector(didReceiveImage:)];
        [imageRequest setDidFailSelector:@selector(didNotReceiveImage:)];
        [imageRequest startAsynchronous];
    }
    else { // cache hit - good!
        *anImagePtr = [newImage retain];    
    }
}

+ (void)didReceiveImage:(ASIHTTPRequest *)request {
    NSLog(@"Image data received.");
    UIImage **anImagePtr = [(NSValue*)[request.userInfo objectForKey:@"imagePtr"] pointerValue];

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    UIImage *newImage = [[UIImage imageWithData:[request responseData]] retain];

    if(!newImage) {
        NSLog(@"UIImageView: LoadImage Failed");
    }
    else {
        *anImagePtr = newImage;
        // check to see if we should flush existing cached items before adding this new item
        if( [cache count] >= MAX_CACHED_IMAGES)
            [cache removeAllObjects];

        [cache setValue:newImage forKey:[request url].description];

        NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
        [nc postNotificationName: @"ImageDidLoad" object: self userInfo:request.userInfo];
    }

    [pool drain];
}

你可以这样调用这段代码:

[ImageFetcher asyncImageFetch:&icon withURL:url];

我也在使用通知,好或不好都会让相应 UIImage 的所有者知道何时应该重新显示 - 在这种情况下,它是在 tableView 上下文中:

- (void)viewDidLoad {
    [super viewDidLoad];
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc addObserver:self selector:@selector(imageDidLoad:) name:@"ImageDidLoad" object:nil];
}

- (void)imageDidLoad:(NSNotification*)notif {
    NSLog(@"Received icon load notification.");
    // reload table view so that new image appears.. would be better if I could
    // only reload the particular UIImageView that holds this image, oh well...
    [self.tableView reloadData];
}

- (void)dealloc {
    NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    [nc removeObserver:self];
        // ...
}

这里还有一个额外的提示:如果您正在缓存大量图像或大型图像,并因此使用了大量内存,则可能需要创建一个“+(void)flushCache”方法,该方法执行“[cache removeAllObjects]”,以便您可以根据需要从didReceiveMemoryWarning方法中调用它。那将释放所有这些内存。 - Steve N

0
你可能还想检查一下HJCache。它带有一个UIImageView兼容的视图类,可以透明地进行所有缓存,并适用于在滚动性能很重要的UITableViewCells中使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接