Skip to content
This repository was archived by the owner on Aug 24, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 44 additions & 4 deletions SAMCache/SAMCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// Copyright (c) 2011-2014 Sam Soffes. All rights reserved.
//

#import <Foundation/Foundation.h>
@import Foundation;

@interface SAMCache : NSObject

Expand Down Expand Up @@ -105,14 +105,38 @@
///----------------------------------------

/**
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.

@param key The key of the object.
*/
- (void)setObject:(id <NSCoding>)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 <NSCoding>)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 <NSCoding>)object forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly withCompletion:(void (^)(BOOL didSave))completionBlock;

/**
Remove an object from the cache.

Expand All @@ -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
Expand Down Expand Up @@ -171,7 +200,7 @@

#if TARGET_OS_IPHONE

#import <UIKit/UIImage.h>
@import UIKit.UIImage;

@interface SAMCache (UIImageAdditions)

Expand Down Expand Up @@ -203,14 +232,25 @@
- (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.

@param key The key of the image.
*/
- (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.

Expand Down
58 changes: 41 additions & 17 deletions SAMCache/SAMCache.m
Original file line number Diff line number Diff line change
Expand Up @@ -166,24 +166,41 @@ - (BOOL)objectExistsForKey:(NSString *)key {
#pragma mark - Adding and Removing Cached Values

- (void)setObject:(id <NSCoding>)object forKey:(NSString *)key {
NSParameterAssert(key);
[self setObject:object forKey:key diskCacheOnly:NO];
}

- (void)setObject:(id <NSCoding>)object forKey:(NSString *)key diskCacheOnly:(BOOL)useDiskCacheOnly {
[self setObject:object forKey:key diskCacheOnly:useDiskCacheOnly withCompletion:nil];
}

- (void)setObject:(id <NSCoding>)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);

Expand All @@ -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

Expand Down Expand Up @@ -262,7 +281,7 @@ - (NSString *)_pathForKey:(NSString *)key {

#if TARGET_OS_IPHONE

#import <UIKit/UIScreen.h>
@import UIKit.UIScreen;

@implementation SAMCache (UIImageAdditions)

Expand Down Expand Up @@ -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]];
Expand Down
36 changes: 36 additions & 0 deletions Tests/SAMCacheTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -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