Skip to content
This repository has been archived by the owner on May 8, 2020. It is now read-only.

Commit

Permalink
Use core data to store the images to disk
Browse files Browse the repository at this point in the history
  • Loading branch information
danielctull committed Nov 23, 2012
1 parent f112707 commit f386a5e
Show file tree
Hide file tree
Showing 14 changed files with 621 additions and 94 deletions.
23 changes: 18 additions & 5 deletions DCTImageCache.xcodeproj/project.pbxproj
Expand Up @@ -21,6 +21,8 @@
24B3EEC915A2EB1F00D5E63C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 24B3EEC815A2EB1F00D5E63C /* AppDelegate.m */; };
24B3EECC15A2EB1F00D5E63C /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 24B3EECB15A2EB1F00D5E63C /* ViewController.m */; };
24B3EECF15A2EB1F00D5E63C /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 24B3EECD15A2EB1F00D5E63C /* ViewController.xib */; };
24F2DB23165FED99008F58D5 /* _DCTImageCacheItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 24F2DB22165FED99008F58D5 /* _DCTImageCacheItem.m */; };
24F2DB27165FEDDC008F58D5 /* __DCTImageCacheItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 24F2DB26165FEDDC008F58D5 /* __DCTImageCacheItem.m */; };
24F7CB52158A1B9D00B2E33E /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24F7CB51158A1B9D00B2E33E /* SenTestingKit.framework */; };
24F7CB54158A1B9D00B2E33E /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24F7CB53158A1B9D00B2E33E /* UIKit.framework */; };
24F7CB55158A1B9D00B2E33E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24F7CB42158A1B9D00B2E33E /* Foundation.framework */; };
Expand Down Expand Up @@ -72,6 +74,10 @@
24B3EEDA15A30F6600D5E63C /* _DCTImageCacheHashStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _DCTImageCacheHashStore.m; sourceTree = "<group>"; };
24B3EEDD15A311D400D5E63C /* _DCTMemoryImageCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _DCTMemoryImageCache.h; sourceTree = "<group>"; };
24B3EEDE15A311D400D5E63C /* _DCTMemoryImageCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _DCTMemoryImageCache.m; sourceTree = "<group>"; };
24F2DB21165FED99008F58D5 /* _DCTImageCacheItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _DCTImageCacheItem.h; sourceTree = "<group>"; };
24F2DB22165FED99008F58D5 /* _DCTImageCacheItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = _DCTImageCacheItem.m; sourceTree = "<group>"; };
24F2DB25165FEDDC008F58D5 /* __DCTImageCacheItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = __DCTImageCacheItem.h; sourceTree = "<group>"; };
24F2DB26165FEDDC008F58D5 /* __DCTImageCacheItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = __DCTImageCacheItem.m; sourceTree = "<group>"; };
24F7CB42158A1B9D00B2E33E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
24F7CB46158A1B9D00B2E33E /* DCTImageCache-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "DCTImageCache-Prefix.pch"; sourceTree = "<group>"; };
24F7CB47158A1B9D00B2E33E /* DCTImageCache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DCTImageCache.h; sourceTree = "<group>"; };
Expand All @@ -83,7 +89,7 @@
24F7CB5D158A1B9D00B2E33E /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
24F7CB5F158A1B9D00B2E33E /* DCTImageCacheTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DCTImageCacheTests.h; sourceTree = "<group>"; };
24F7CB60158A1B9D00B2E33E /* DCTImageCacheTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DCTImageCacheTests.m; sourceTree = "<group>"; };
24FCFA94165FEAB3006208A7 /* DCTImageCacheBundle.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DCTImageCacheBundle.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
24FCFA94165FEAB3006208A7 /* DCTImageCache.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DCTImageCache.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
24FCFA95165FEAB3006208A7 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
24FCFA99165FEAB3006208A7 /* DCTImageCacheBundle-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DCTImageCacheBundle-Info.plist"; sourceTree = "<group>"; };
24FCFAA5165FEB25006208A7 /* DCTImageCache.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = DCTImageCache.xcdatamodel; sourceTree = "<group>"; };
Expand Down Expand Up @@ -171,7 +177,7 @@
24F7CB50158A1B9D00B2E33E /* DCTImageCacheTests.octest */,
24B3EEB815A2EB1F00D5E63C /* DCTImageCaccheDemo.app */,
2437B67715B97BA600750117 /* libDCTImageCache.a */,
24FCFA94165FEAB3006208A7 /* DCTImageCacheBundle.bundle */,
24FCFA94165FEAB3006208A7 /* DCTImageCache.bundle */,
);
name = Products;
sourceTree = "<group>";
Expand All @@ -195,6 +201,10 @@
24F7CB49158A1B9D00B2E33E /* DCTImageCache.m */,
24B3EED515A30EEB00D5E63C /* _DCTDiskImageCache.h */,
24B3EED615A30EEB00D5E63C /* _DCTDiskImageCache.m */,
24F2DB25165FEDDC008F58D5 /* __DCTImageCacheItem.h */,
24F2DB26165FEDDC008F58D5 /* __DCTImageCacheItem.m */,
24F2DB21165FED99008F58D5 /* _DCTImageCacheItem.h */,
24F2DB22165FED99008F58D5 /* _DCTImageCacheItem.m */,
24B3EEDD15A311D400D5E63C /* _DCTMemoryImageCache.h */,
24B3EEDE15A311D400D5E63C /* _DCTMemoryImageCache.m */,
24B3EED915A30F6600D5E63C /* _DCTImageCacheHashStore.h */,
Expand Down Expand Up @@ -318,7 +328,7 @@
);
name = DCTImageCacheBundle;
productName = DCTImageCacheBundle;
productReference = 24FCFA94165FEAB3006208A7 /* DCTImageCacheBundle.bundle */;
productReference = 24FCFA94165FEAB3006208A7 /* DCTImageCache.bundle */;
productType = "com.apple.product-type.bundle";
};
/* End PBXNativeTarget section */
Expand Down Expand Up @@ -402,6 +412,8 @@
2437B68515B97BD700750117 /* _DCTDiskImageCache.m in Sources */,
2437B68615B97BD700750117 /* _DCTMemoryImageCache.m in Sources */,
2437B68715B97BD700750117 /* _DCTImageCacheHashStore.m in Sources */,
24F2DB23165FED99008F58D5 /* _DCTImageCacheItem.m in Sources */,
24F2DB27165FEDDC008F58D5 /* __DCTImageCacheItem.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -625,7 +637,7 @@
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.8;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
PRODUCT_NAME = DCTImageCache;
WRAPPER_EXTENSION = bundle;
};
name = Debug;
Expand All @@ -643,7 +655,7 @@
INFOPLIST_FILE = "DCTImageCacheBundle/DCTImageCacheBundle-Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_NAME = "$(TARGET_NAME)";
PRODUCT_NAME = DCTImageCache;
WRAPPER_EXTENSION = bundle;
};
name = Release;
Expand Down Expand Up @@ -694,6 +706,7 @@
24FCFA9F165FEAB3006208A7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

Expand Down
4 changes: 2 additions & 2 deletions DCTImageCache/DCTImageCache.m
Expand Up @@ -19,7 +19,7 @@ @implementation DCTImageCache {
}

#pragma mark NSObject

/*
+ (void)initialize {
@autoreleasepool {
NSDate *now = [NSDate date];
Expand All @@ -46,7 +46,7 @@ + (void)initialize {
}];
}
}

*/
#pragma mark DCTImageCache

- (id<DCTImageCache>)diskCache {
Expand Down
4 changes: 2 additions & 2 deletions DCTImageCache/_DCTDiskImageCache.h
Expand Up @@ -11,7 +11,7 @@

@interface _DCTDiskImageCache : NSObject <DCTImageCache>
- (id)initWithPath:(NSString *)path;
- (void)fetchAttributesForImageWithKey:(NSString *)key size:(CGSize)size handler:(void (^)(NSDictionary *))handler;
/*- (void)fetchAttributesForImageWithKey:(NSString *)key size:(CGSize)size handler:(void (^)(NSDictionary *))handler;
- (void)enumerateKeysUsingBlock:(void (^)(NSString *key, BOOL *stop))block;
- (void)enumerateSizesForKey:(NSString *)key usingBlock:(void (^)(CGSize size, BOOL *stop))block;
@end
*/@end
162 changes: 79 additions & 83 deletions DCTImageCache/_DCTDiskImageCache.m
Expand Up @@ -7,95 +7,126 @@
//

#import "_DCTDiskImageCache.h"
#import "_DCTImageCacheHashStore.h"
#import "_DCTImageCacheItem.h"
#import <CoreData/CoreData.h>

@implementation _DCTDiskImageCache {
__strong NSString *_path;
__strong _DCTImageCacheHashStore *_hashStore;
__strong NSFileManager *_fileManager;
__strong NSOperationQueue *_queue;

NSManagedObjectContext *_managedObjectContext;
//NSManagedObjectContext *savingContext;
//NSManagedObjectContext *fetchingContext;
}

+ (NSBundle *)bundle {
static NSBundle *bundle;
static dispatch_once_t bundleToken;
dispatch_once(&bundleToken, ^{
NSDirectoryEnumerator *enumerator = [[NSFileManager new] enumeratorAtURL:[[NSBundle mainBundle] bundleURL]
includingPropertiesForKeys:nil
options:NSDirectoryEnumerationSkipsHiddenFiles
errorHandler:NULL];

for (NSURL *URL in enumerator)
if ([[URL lastPathComponent] isEqualToString:@"DCTImageCache.bundle"])
bundle = [NSBundle bundleWithURL:URL];
});

return bundle;
}

- (id)initWithPath:(NSString *)path {
if (!(self = [super init])) return nil;

_queue = [NSOperationQueue new];
[_queue setMaxConcurrentOperationCount:1];

[self _performBlock:^{
_path = [path copy];
_hashStore = [[_DCTImageCacheHashStore alloc] initWithPath:path];
_fileManager = [NSFileManager new];
[_fileManager createDirectoryAtPath:_path withIntermediateDirectories:YES attributes:nil error:nil];
}];
NSURL *storeURL = [[[NSURL alloc] initFileURLWithPath:path] URLByAppendingPathComponent:@"store"];
NSURL *modelURL = [[[self class] bundle] URLForResource:@"DCTImageCache" withExtension:@"momd"];
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
[coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:NULL];
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
_managedObjectContext.persistentStoreCoordinator = coordinator;

return self;
}

- (void)removeAllImages {
[self _performBlock:^{
[_fileManager removeItemAtPath:_path error:nil];
[_fileManager createDirectoryAtPath:_path withIntermediateDirectories:YES attributes:nil error:nil];
[_managedObjectContext performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[_DCTImageCacheItem entityName]];
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:NULL];
for (_DCTImageCacheItem *item in items) [_managedObjectContext deleteObject:item];
}];
}

- (void)removeImageForKey:(NSString *)key size:(CGSize)size {
[self _performBlock:^{
NSString *path = [self _pathForKey:key size:size];
[_fileManager removeItemAtPath:path error:nil];
[_managedObjectContext performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[_DCTImageCacheItem entityName]];
NSPredicate *keyPredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.key, key];
NSPredicate *sizePredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.sizeString, NSStringFromCGSize(size)];
fetchRequest.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[keyPredicate, sizePredicate]];
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:NULL];
for (_DCTImageCacheItem *item in items) [_managedObjectContext deleteObject:item];
}];
}

- (void)removeAllImagesForKey:(NSString *)key {
[self _performBlock:^{
NSString *directoryPath = [self _pathForKey:key];
[_fileManager removeItemAtPath:directoryPath error:nil];
[_hashStore removeHashForKey:key];
[_managedObjectContext performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[_DCTImageCacheItem entityName]];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.key, key];
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:NULL];
for (_DCTImageCacheItem *item in items) [_managedObjectContext deleteObject:item];
}];
}

- (UIImage *)imageForKey:(NSString *)key size:(CGSize)size {

__block UIImage *image = nil;

[self _performBlockAndWait:^{
image = [self _imageForKey:key size:size];
__block UIImage *image;
[_managedObjectContext performBlockAndWait:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[_DCTImageCacheItem entityName]];
NSPredicate *keyPredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.key, key];
NSPredicate *sizePredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.sizeString, NSStringFromCGSize(size)];
fetchRequest.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[keyPredicate, sizePredicate]];
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:NULL];
_DCTImageCacheItem *item = [items lastObject];
image = [UIImage imageWithData:item.imageData];
}];

return image;
}

- (BOOL)hasImageForKey:(NSString *)key size:(CGSize)size {

__block BOOL hasImage = NO;

[self _performBlockAndWait:^{
NSString *imagePath = [self _pathForKey:key size:size];
hasImage = [_fileManager fileExistsAtPath:imagePath];
[_managedObjectContext performBlockAndWait:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[_DCTImageCacheItem entityName]];
NSPredicate *keyPredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.key, key];
NSPredicate *sizePredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.sizeString, NSStringFromCGSize(size)];
fetchRequest.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[keyPredicate, sizePredicate]];
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:NULL];
hasImage = items.count > 0;
}];

return hasImage;
return hasImage;
}

- (void)fetchImageForKey:(NSString *)key size:(CGSize)size handler:(void (^)(UIImage *))handler {
[self _performWithPriority:NSOperationQueuePriorityVeryHigh block:^{
UIImage *image = [self _imageForKey:key size:size];
[_managedObjectContext performBlock:^{
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:[_DCTImageCacheItem entityName]];
NSPredicate *keyPredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.key, key];
NSPredicate *sizePredicate = [NSPredicate predicateWithFormat:@"%K == %@", _DCTImageCacheItemAttributes.sizeString, NSStringFromCGSize(size)];
fetchRequest.predicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[keyPredicate, sizePredicate]];
NSArray *items = [_managedObjectContext executeFetchRequest:fetchRequest error:NULL];
_DCTImageCacheItem *item = [items lastObject];
UIImage *image = [UIImage imageWithData:item.imageData];
handler(image);
}];
}

- (void)setImage:(UIImage *)image forKey:(NSString *)key size:(CGSize)size {
[self _performBlock:^{
NSString *path = [self _pathForKey:key];
NSString *imagePath = [self _pathForKey:key size:size];

if (![_fileManager fileExistsAtPath:path])
[_fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];

[_fileManager createFileAtPath:imagePath contents:UIImagePNGRepresentation(image) attributes:nil];
[_managedObjectContext performBlock:^{
_DCTImageCacheItem *item = [_DCTImageCacheItem insertInManagedObjectContext:_managedObjectContext];
item.key = key;
item.sizeString = NSStringFromCGSize(size);
item.imageData = UIImagePNGRepresentation(image);
[_managedObjectContext save:NULL];
}];
}

/*
- (void)fetchAttributesForImageWithKey:(NSString *)key size:(CGSize)size handler:(void (^)(NSDictionary *))handler {
[self _performBlock:^{
NSString *path = [self _pathForKey:key size:size];
Expand All @@ -121,41 +152,6 @@ - (void)enumerateSizesForKey:(NSString *)key usingBlock:(void (^)(CGSize size, B
block(CGSizeFromString(filename), stop);
}];
}];
}

#pragma mark Internal

- (void)_performBlock:(void(^)())block {
[_queue addOperationWithBlock:block];
}

- (void)_performWithPriority:(NSOperationQueuePriority)priority block:(void(^)())block {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
[blockOperation setQueuePriority:priority];
[_queue addOperation:blockOperation];
}

- (void)_performBlockAndWait:(void(^)())block {
NSBlockOperation *blockOperation = [NSBlockOperation blockOperationWithBlock:block];
[blockOperation setQueuePriority:NSOperationQueuePriorityVeryHigh];
[_queue addOperation:blockOperation];
[blockOperation waitUntilFinished];
}

- (UIImage *)_imageForKey:(NSString *)key size:(CGSize)size {
NSString *imagePath = [self _pathForKey:key size:size];
NSData *data = [_fileManager contentsAtPath:imagePath];
return [UIImage imageWithData:data];
}

- (NSString *)_pathForKey:(NSString *)key size:(CGSize)size {
NSString *path = [self _pathForKey:key];
return [path stringByAppendingPathComponent:NSStringFromCGSize(size)];
}

- (NSString *)_pathForKey:(NSString *)key {
NSString *hash = [_hashStore hashForKey:key];
return [_path stringByAppendingPathComponent:hash];
}
}*/

@end
5 changes: 5 additions & 0 deletions DCTImageCache/_DCTImageCacheItem.h
@@ -0,0 +1,5 @@
#import "__DCTImageCacheItem.h"

@interface _DCTImageCacheItem : __DCTImageCacheItem {}
// Custom logic goes here.
@end
7 changes: 7 additions & 0 deletions DCTImageCache/_DCTImageCacheItem.m
@@ -0,0 +1,7 @@
#import "_DCTImageCacheItem.h"

@implementation _DCTImageCacheItem

// Custom logic goes here.

@end

0 comments on commit f386a5e

Please sign in to comment.