diff --git a/SAMCache/SAMCache.h b/SAMCache/SAMCache.h index 5221b9e..649ee7d 100755 --- a/SAMCache/SAMCache.h +++ b/SAMCache/SAMCache.h @@ -6,7 +6,7 @@ // Copyright (c) 2011-2014 Sam Soffes. All rights reserved. // -#import +@import Foundation; @interface SAMCache : NSObject @@ -105,7 +105,7 @@ ///---------------------------------------- /** - Synchronously set an object in the cache for a given key. + Synchronously set an object in the memory cache for a given key, while asynchronously writing to the disk cache. Uses both memory and disk cache. @param object The object to store in the cache. @@ -113,6 +113,30 @@ */ - (void)setObject:(id )object forKey:(NSString *)key; +/** + Synchronously set an object in the memory cache for a given key, while asynchronously writing to the disk cache. Has an option to only write asynchronously to the disk cache. + + @param object The object to store in the cache. + + @param key The key of the object. + + @param useDiskCacheOnly A value indicating whether or not to store the object in memory or only write object to disk cache location in an asynchronous fashion. + */ +- (void)setObject:(id )object forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly; + +/** + Synchronously set an object in the memory cache for a given key, while asynchronously writing to the disk cache. Has an option to only write asynchronously to the disk cache, along with a completion block for the asynchronous write operation. + + @param object The object to store in the cache. + + @param key The key of the object. + + @param useDiskCacheOnly A value indicating whether or not to store the object in memory or only write object to disk cache location in an asynchronous fashion. + + @param completionBlock A callback block that indicates that all operations (synchronous and asynchronous) have been completed, with an indication of success for the disk wrigin operation. + */ +- (void)setObject:(id )object forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly withCompletion:(void (^)(BOOL didSave))completionBlock; + /** Remove an object from the cache. @@ -125,6 +149,11 @@ */ - (void)removeAllObjects; +/** + Removes all cached objects from the memory cache. Does not remove disk-cache + entries. + */ +- (void)flushMemoryCache; ///------------------------------- /// @name Accessing the Disk Cache @@ -171,7 +200,7 @@ #if TARGET_OS_IPHONE -#import +@import UIKit.UIImage; @interface SAMCache (UIImageAdditions) @@ -203,7 +232,7 @@ - (void)imageForKey:(NSString *)key usingBlock:(void (^)(UIImage *image))block; /** - Synchronously store a PNG representation of an image in the cache for a given key. + Synchronously store a PNG representation of an image in the cache for a given key. Uses both memory and disk cache. @param image The image to store in the cache. @@ -211,6 +240,17 @@ */ - (void)setImage:(UIImage *)image forKey:(NSString *)key; +/** + Synchronously store a PNG representation of an image in the cache for a given key. + + @param image The image to store in the cache. + + @param key The key of the image. + + @param useDiskCacheOnly A value indicating whether or not to store the object in memory or only write object to disk cache location. + */ +- (void)setImage:(UIImage *)image forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly; + /** Synchronously check if an image exists in the cache without retriving it. diff --git a/SAMCache/SAMCache.m b/SAMCache/SAMCache.m index 0c54b2e..8334df5 100755 --- a/SAMCache/SAMCache.m +++ b/SAMCache/SAMCache.m @@ -166,24 +166,41 @@ - (BOOL)objectExistsForKey:(NSString *)key { #pragma mark - Adding and Removing Cached Values - (void)setObject:(id )object forKey:(NSString *)key { - NSParameterAssert(key); + [self setObject:object forKey:key diskCacheOnly:NO]; +} + +- (void)setObject:(id )object forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly { + [self setObject:object forKey:key diskCacheOnly:useDiskCacheOnly withCompletion:nil]; +} +- (void)setObject:(id )object forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly withCompletion:(void (^)(BOOL didSave))completionBlock { + NSParameterAssert(key); + // If there's no object, delete the key. if (!object) { [self removeObjectForKey:key]; return; } - - // Save to memory cache - [self.cache setObject:object forKey:key]; - + + if (useDiskCacheOnly == NO) { + // Save to memory cache + [self.cache setObject:object forKey:key]; + } + + __weak SAMCache *weakSelf = self; + dispatch_async(self.diskQueue, ^{ + __strong SAMCache *strongSelf = weakSelf; + // Save to disk cache - [NSKeyedArchiver archiveRootObject:object toFile:[self _pathForKey:key]]; + BOOL didSave = [NSKeyedArchiver archiveRootObject:object toFile:[strongSelf _pathForKey:key]]; + + if (completionBlock) { + completionBlock(didSave); + } }); } - - (void)removeObjectForKey:(NSString *)key { NSParameterAssert(key); @@ -202,10 +219,12 @@ - (void)removeAllObjects { for (NSString *path in [self.fileManager contentsOfDirectoryAtPath:self.directory error:nil]) { [self.fileManager removeItemAtPath:[self.directory stringByAppendingPathComponent:path] error:nil]; } - [self.fileManager removeItemAtPath:self.directory error:nil]; }); } +- (void)flushMemoryCache { + [self.cache removeAllObjects]; +} #pragma mark - Accessing the Disk Cache @@ -262,7 +281,7 @@ - (NSString *)_pathForKey:(NSString *)key { #if TARGET_OS_IPHONE -#import +@import UIKit.UIScreen; @implementation SAMCache (UIImageAdditions) @@ -319,28 +338,33 @@ - (void)imageForKey:(NSString *)key usingBlock:(void (^)(UIImage *image))block { - (void)setImage:(UIImage *)image forKey:(NSString *)key { - NSParameterAssert(key); + [self setImage:image forKey:key diskCacheOnly:NO]; +} +- (void)setImage:(UIImage *)image forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly { + NSParameterAssert(key); + // If there's no image, delete the key. if (!image) { [self removeObjectForKey:key]; return; } - + key = [[self class] _keyForImageKey:key]; - + dispatch_async(self.diskQueue, ^{ NSString *path = [self _pathForKey:key]; - - // Save to memory cache - [self.cache setObject:image forKey:key]; - + + if (useDiskCacheOnly == NO) { + // Save to memory cache + [self.cache setObject:image forKey:key]; + } + // Save to disk cache [UIImagePNGRepresentation(image) writeToFile:path atomically:YES]; }); } - - (BOOL)imageExistsForKey:(NSString *)key { NSParameterAssert(key); return [self objectExistsForKey:[[self class] _keyForImageKey:key]]; diff --git a/Tests/SAMCacheTests.m b/Tests/SAMCacheTests.m index e021ba6..22a0fb4 100644 --- a/Tests/SAMCacheTests.m +++ b/Tests/SAMCacheTests.m @@ -69,4 +69,40 @@ - (void)testReadingWithSubscript { XCTAssertEqualObjects(@"subread", self.cache[@"subscriptRead"], @"Reading an object with a subscript"); } +- (void)testAddingToDiskCacheOnly { + [self.cache setObject:@42 forKey:@"answer" diskCacheOnly:YES]; + + XCTAssertNil([self.cache.cache objectForKey:@"answer"]); + + XCTAssertEqualObjects(@42, [self.cache objectForKey:@"answer"], @"Reading from disk cache"); + + XCTAssertNotNil([self.cache.cache objectForKey:@"answer"]); +} + +- (void)testAddingToDiskCacheOnlyWithCallback { + XCTestExpectation *diskWriteExpectation = [self expectationWithDescription:@"object written to disk"]; + + __weak SAMCacheTests *weakSelf = self; + + [weakSelf.cache setObject:@42 forKey:@"answer" diskCacheOnly:YES withCompletion:^(BOOL didSave) { + XCTAssert(didSave); + [diskWriteExpectation fulfill]; + }]; + + [self waitForExpectationsWithTimeout:1 handler:nil]; +} + +- (void)testFlushMemoryCache { + [self.cache setObject:@42 forKey:@"answer"]; + XCTAssertEqualObjects(@42, [self.cache objectForKey:@"answer"], @"Reading from memory cache"); + + [self.cache flushMemoryCache]; + + XCTAssertNil([self.cache.cache objectForKey:@"answer"]); + + XCTAssertEqualObjects(@42, [self.cache objectForKey:@"answer"], @"Reading from disk cache"); + + XCTAssertNotNil([self.cache.cache objectForKey:@"answer"]); +} + @end