I was trying to load and cache images in my app for showing on UITableView. The original concepts looks like:
if (image in cache){
show cached image;
}else{
if (image in file){
show filed image;
}else{
load image from web asynchronously;
if (finished){
store in cache;
store in file;
}
}
}
Here's the whole class I created to handle image storage:
ImageStore.h
@interface ImageStore : NSObject
@property (nonatomic, strong) NSCache *imageCache;
+ (ImageStore*)sharedImageStore;
//cache
- (void)writeImage: (UIImage*)image ToCacheForKey: (NSString*)imageURL;
- (UIImage*)readImageFromCacheForKey: (NSString*)imageURL;
- (BOOL)imageCachedForKey: (NSString*)imageURL;
//file
- (BOOL)writeImage: (UIImage*)image ToPlistFileForKey: (NSString*)imageURL;
- (UIImage*)readImageFromPlistFileForKey: (NSString*)imageURL;
- (BOOL)imageFiledInPlistForKey: (NSString*)imageURL;
@end
ImageStore.m
#import "ImageStore.h"
@implementation ImageStore
static ImageStore* sharedImageStore;
NSMutableDictionary* plistDictioary;
+(ImageStore*)sharedImageStore
{
if (sharedImageStore == nil){
sharedImageStore= [[self alloc] init];
}
return sharedImageStore;
}
+(id)alloc
{
{
NSAssert(sharedImageStore == nil, @"second singleton");
sharedImageStore = [super alloc];
return sharedImageStore;
}
return nil;
}
-(id)init
{
self = [super init];
if (self != nil)
{
_imageCache = [[NSCache alloc] init];
}
return self;
}
#pragma mark - Cache
- (void)writeImage:(UIImage *)image ToCacheForKey:(NSString *)imageURL{
if(image != nil){
[_imageCache setObject:image forKey:imageURL];
}else{
NSLog(@"NIL image:%@ ",imageURL);
}
}
- (UIImage *)readImageFromCacheForKey:(NSString *)imageURL{
return [_imageCache objectForKey:imageURL];
}
- (BOOL)imageCachedForKey:(NSString *)imageURL{
if ([_imageCache objectForKey:imageURL] == nil)
{
return false;
}
return true;
}
#pragma mark - File
- (BOOL)writeImage:(UIImage*)image ToPlistFileForKey: (NSString*)imageURL{
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:image];
if (plistDictioary == nil) {
plistDictioary = [NSMutableDictionary new];
}
[plistDictioary setObject:data forKey:imageURL];
BOOL didWriteSuccessfull = [plistDictioary writeToFile:[self getPlistFilePath] atomically:YES];
return didWriteSuccessfull;
}
- (UIImage *)readImageFromPlistFileForKey:(NSString *)imageURL{
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[self getPlistFilePath]];
NSData * data = [dict objectForKey:imageURL];
UIImage * image = [UIImage imageWithData:data];
return image;
}
- (BOOL)imageFiledInPlistForKey:(NSString *)imageURL{
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[self getPlistFilePath]];
if ([dict objectForKey:imageURL] == nil) {
return NO;
}else{
return YES;
}
}
- (NSString*)getPlistFilePath{
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * documentsDirectory = [paths objectAtIndex:0];
NSString * path = [documentsDirectory stringByAppendingPathComponent:@"Images.plist"];
return path;
}
@end
Implementation in tableView:cellForRowAtIndex
:
if ([[ImageStore sharedImageStore]imageCachedForKey:[[dataArray objectAtIndex:indexPath.row] objectForKey:@"image"]]) {
//cache
NSLog(@"cached");
cell.image = [[ImageStore sharedImageStore] readImageFromCacheForKey:[[dataArray objectAtIndex:indexPath.row] objectForKey:@"image"]];
}else{
NSLog(@"nothing cached");
if ([[ImageStore sharedImageStore] imageFiledInPlistForKey:[[dataArray objectAtIndex:indexPath.row] objectForKey:@"image"]]) {
//plist file
cell.image = [[ImageStore sharedImageStore] readImageFromPlistFileForKey:[[dataArray objectAtIndex:indexPath.row] objectForKey:@"image"]];
}else{
//loading with UIImage category async method (see below)
[UIImage loadFromURL:[NSURL URLWithString:[[dataArray objectAtIndex:indexPath.row] objectForKey:@"image"]] completionBlock:^(BOOL succeeded, UIImage *image) {
if (succeeded){
cell.image = image;
}
}];
}
}
EDIT
Downloaded Images stored in the UIImage Category:
+ (void)loadFromURL:(NSURL *)url completionBlock:(void (^)(BOOL succeeded, UIImage *image))completionBlock
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if ( !error )
{
UIImage *image = [[UIImage alloc] initWithData:data];
[[ImageStore sharedImageStore] writeImage:image ToCacheForKey:[url absoluteString]];//write to NSCache
[[ImageStore sharedImageStore] writeImage:image ToPlistFileForKey:[url absoluteString]];//write to Plist
completionBlock(YES,image);
} else{
completionBlock(NO,nil);
}
}];
}
(Sorry for the long method naming.)
The console returns "nothing cached" for every onscreen cell, and sometimes the scrolling become stutter when checking cache and file.
I've been trying handy caching for a couple of days while there's already a bunch of useful image caching library. It'd be nice to know what am I doing wrong with this! Thanks for any advice.