Updating example

This commit is contained in:
Garrett Moon
2016-07-22 16:47:27 -07:00
parent 3e6d603cc2
commit f9e939b374
16 changed files with 3055 additions and 2779 deletions

View File

@@ -23,12 +23,12 @@ PODS:
- libwebp/utils (0.5.0):
- libwebp/core
- libwebp/webp (0.5.0)
- PINCache (2.2.2)
- PINRemoteImage/Core (2.1.2):
- PINCache (>= 2.1)
- PINRemoteImage/iOS (2.1.2):
- PINCache (3.0.0-beta)
- PINRemoteImage/Core (3.0.0-beta.2):
- PINCache (>= 3.0.0-beta)
- PINRemoteImage/iOS (3.0.0-beta.2):
- PINRemoteImage/Core
- PINRemoteImage/WebP (2.1.2):
- PINRemoteImage/WebP (3.0.0-beta.2):
- libwebp
- PINRemoteImage/Core
@@ -43,7 +43,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
libwebp: 68ba2001ba6806ee52837ebd77a551a0b6567e4e
PINCache: 078426d356ab95ef875e9e62e5c35a2ea3333c28
PINRemoteImage: 49c514f94a8a51ce871aebba2ea7e5a10598af3b
PINCache: 9b66261ade3d4feb272df4b4dd2e035469909448
PINRemoteImage: eb41cb4fa9c4434ca0911bddf1eb12e70a0b7f1f
COCOAPODS: 0.39.0

View File

@@ -0,0 +1 @@
../../../PINCache/PINCache/PINCacheObjectSubscripting.h

View File

@@ -0,0 +1 @@
../../../PINCache/PINCache/PINCacheObjectSubscripting.h

View File

@@ -1,6 +1,6 @@
{
"name": "PINRemoteImage",
"version": "2.1.2",
"version": "3.0.0-beta.2",
"summary": "A thread safe, performant, feature rich image fetcher",
"homepage": "https://github.com/pinterest/PINRemoteImage",
"license": "Apache 2.0",
@@ -9,12 +9,11 @@
},
"source": {
"git": "https://github.com/pinterest/PINRemoteImage.git",
"tag": "2.1.2"
"tag": "3.0.0-beta.2"
},
"social_media_url": "https://twitter.com/garrettmoon",
"platforms": {
"ios": "6.0",
"osx": "10.8",
"ios": "7.0",
"tvos": "9.0"
},
"requires_arc": true,
@@ -22,6 +21,11 @@
"subspecs": [
{
"name": "Core",
"platforms": {
"ios": "7.0",
"tvos": "9.0",
"osx": "10.9"
},
"source_files": "Pod/Classes/**/*.{h,m}",
"exclude_files": [
"Pod/Classes/Image Categories/FLAnimatedImageView+PINRemoteImage.h",
@@ -34,13 +38,16 @@
],
"dependencies": {
"PINCache": [
">=2.1"
">=3.0.0-beta"
]
}
},
{
"name": "iOS",
"platforms": "ios",
"platforms": {
"ios": "7.0",
"tvos": "9.0"
},
"dependencies": {
"PINRemoteImage/Core": [
@@ -50,23 +57,26 @@
},
{
"name": "OSX",
"platforms": "osx",
"platforms": {
"osx": "10.9"
},
"dependencies": {
"PINRemoteImage/Core": [
]
},
"frameworks": "Cocoa"
"frameworks": [
"Cocoa",
"CoreServices"
]
},
{
"name": "tvOS",
"platforms": "tvos",
"dependencies": {
"PINRemoteImage/Core": [
"PINRemoteImage/iOS": [
]
},
"frameworks": "UIKit"
}
},
{
"name": "FLAnimatedImage",

14
Example/Pods/Manifest.lock generated vendored
View File

@@ -23,12 +23,12 @@ PODS:
- libwebp/utils (0.5.0):
- libwebp/core
- libwebp/webp (0.5.0)
- PINCache (2.2.2)
- PINRemoteImage/Core (2.1.2):
- PINCache (>= 2.1)
- PINRemoteImage/iOS (2.1.2):
- PINCache (3.0.0-beta)
- PINRemoteImage/Core (3.0.0-beta.2):
- PINCache (>= 3.0.0-beta)
- PINRemoteImage/iOS (3.0.0-beta.2):
- PINRemoteImage/Core
- PINRemoteImage/WebP (2.1.2):
- PINRemoteImage/WebP (3.0.0-beta.2):
- libwebp
- PINRemoteImage/Core
@@ -43,7 +43,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
libwebp: 68ba2001ba6806ee52837ebd77a551a0b6567e4e
PINCache: 078426d356ab95ef875e9e62e5c35a2ea3333c28
PINRemoteImage: 49c514f94a8a51ce871aebba2ea7e5a10598af3b
PINCache: 9b66261ade3d4feb272df4b4dd2e035469909448
PINRemoteImage: eb41cb4fa9c4434ca0911bddf1eb12e70a0b7f1f
COCOAPODS: 0.39.0

View File

@@ -14,15 +14,19 @@ NS_ASSUME_NONNULL_BEGIN
/**
A callback block which provides only the cache as an argument
*/
typedef void (^PINCacheBlock)(PINCache *cache);
/**
A callback block which provides the cache, key and object as arguments
*/
typedef void (^PINCacheObjectBlock)(PINCache *cache, NSString *key, id __nullable object);
/**
A callback block which provides a BOOL value as argument
*/
typedef void (^PINCacheObjectContainmentBlock)(BOOL containsObject);
/**
`PINCache` is a thread safe key/value store designed for persisting temporary objects that are expensive to
reproduce, such as downloaded data or the results of slow processing. It is comprised of two self-similar
@@ -40,7 +44,7 @@ typedef void (^PINCacheObjectBlock)(PINCache *cache, NSString *key, id __nullabl
@warning when using in extension or watch extension, define PIN_APP_EXTENSIONS=1
*/
@interface PINCache : NSObject
@interface PINCache : NSObject <PINCacheObjectSubscripting>
#pragma mark -
/// @name Core
@@ -106,6 +110,17 @@ typedef void (^PINCacheObjectBlock)(PINCache *cache, NSString *key, id __nullabl
#pragma mark -
/// @name Asynchronous Methods
/**
This method determines whether an object is present for the given key in the cache. This method returns immediately
and executes the passed block after the object is available, potentially in parallel with other blocks on the
<concurrentQueue>.
@see containsObjectForKey:
@param key The key associated with the object.
@param block A block to be executed concurrently after the containment check happened
*/
- (void)containsObjectForKey:(NSString *)key block:(PINCacheObjectContainmentBlock)block;
/**
Retrieves the object for the specified key. This method returns immediately and executes the passed
block after the object is available, potentially in parallel with other blocks on the <concurrentQueue>.
@@ -154,6 +169,15 @@ typedef void (^PINCacheObjectBlock)(PINCache *cache, NSString *key, id __nullabl
#pragma mark -
/// @name Synchronous Methods
/**
This method determines whether an object is present for the given key in the cache.
@see containsObjectForKey:block:
@param key The key associated with the object.
@result YES if an object is present for the given key in the cache, otherwise NO.
*/
- (BOOL)containsObjectForKey:(NSString *)key;
/**
Retrieves the object for the specified key. This method blocks the calling thread until the object is available.
Uses a semaphore to achieve synchronicity on the disk cache.

View File

@@ -46,7 +46,7 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
if (self = [super init]) {
_name = [name copy];
NSString *queueName = [[NSString alloc] initWithFormat:@"%@.%p", PINCachePrefix, self];
NSString *queueName = [[NSString alloc] initWithFormat:@"%@.%p", PINCachePrefix, (void *)self];
_concurrentQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@ Asynchronous Queue", queueName] UTF8String], DISPATCH_QUEUE_CONCURRENT);
_diskCache = [[PINDiskCache alloc] initWithName:_name rootPath:rootPath];
@@ -57,7 +57,7 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
- (NSString *)description
{
return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINCachePrefix, _name, self];
return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINCachePrefix, _name, (void *)self];
}
+ (instancetype)sharedCache
@@ -74,6 +74,22 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
#pragma mark - Public Asynchronous Methods -
- (void)containsObjectForKey:(NSString *)key block:(PINCacheObjectContainmentBlock)block
{
if (!key || !block) {
return;
}
__weak PINCache *weakSelf = self;
dispatch_async(_concurrentQueue, ^{
PINCache *strongSelf = weakSelf;
BOOL containsObject = [strongSelf containsObjectForKey:key];
block(containsObject);
});
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow"
@@ -88,38 +104,27 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
PINCache *strongSelf = weakSelf;
if (!strongSelf)
return;
__weak PINCache *weakSelf = strongSelf;
[strongSelf->_memoryCache objectForKey:key block:^(PINMemoryCache *memoryCache, NSString *memoryCacheKey, id memoryCacheObject) {
PINCache *strongSelf = weakSelf;
if (!strongSelf)
return;
if (memoryCacheObject) {
[strongSelf->_diskCache fileURLForKey:memoryCacheKey block:^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> diskCacheObject, NSURL *fileURL) {
// update the access time on disk
}];
__weak PINCache *weakSelf = strongSelf;
[strongSelf->_diskCache fileURLForKey:memoryCacheKey block:NULL];
dispatch_async(strongSelf->_concurrentQueue, ^{
PINCache *strongSelf = weakSelf;
if (strongSelf)
block(strongSelf, memoryCacheKey, memoryCacheObject);
});
} else {
__weak PINCache *weakSelf = strongSelf;
[strongSelf->_diskCache objectForKey:memoryCacheKey block:^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> diskCacheObject, NSURL *fileURL) {
[strongSelf->_diskCache objectForKey:memoryCacheKey block:^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> diskCacheObject) {
PINCache *strongSelf = weakSelf;
if (!strongSelf)
return;
[strongSelf->_memoryCache setObject:diskCacheObject forKey:diskCacheKey block:nil];
__weak PINCache *weakSelf = strongSelf;
dispatch_async(strongSelf->_concurrentQueue, ^{
PINCache *strongSelf = weakSelf;
if (strongSelf)
@@ -151,7 +156,7 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
dispatch_group_leave(group);
};
diskBlock = ^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> memoryCacheObject, NSURL *memoryCacheFileURL) {
diskBlock = ^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> memoryCacheObject) {
dispatch_group_leave(group);
};
}
@@ -191,7 +196,7 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
dispatch_group_leave(group);
};
diskBlock = ^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> memoryCacheObject, NSURL *memoryCacheFileURL) {
diskBlock = ^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> memoryCacheObject) {
dispatch_group_leave(group);
};
}
@@ -303,6 +308,14 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
return byteCount;
}
- (BOOL)containsObjectForKey:(NSString *)key
{
if (!key)
return NO;
return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
}
- (__nullable id)objectForKey:(NSString *)key
{
if (!key)
@@ -332,6 +345,16 @@ static NSString * const PINCacheSharedName = @"PINCacheShared";
[_diskCache setObject:object forKey:key];
}
- (id)objectForKeyedSubscript:(NSString *)key
{
return [self objectForKey:key];
}
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key
{
[self setObject:obj forKey:key];
}
- (void)removeObjectForKey:(NSString *)key
{
if (!key)

View File

@@ -0,0 +1,31 @@
//
// PINCacheObjectSubscripting.h
// PINCache
//
// Created by Rocir Marcos Leite Santiago on 4/2/16.
// Copyright © 2016 Pinterest. All rights reserved.
//
#import <Foundation/Foundation.h>
@protocol PINCacheObjectSubscripting <NSObject>
@required
/**
This method enables using literals on the receiving object, such as `id object = cache[@"key"];`.
@param key The key associated with the object.
@result The object for the specified key.
*/
- (id)objectForKeyedSubscript:(NSString *)key;
/**
This method enables using literals on the receiving object, such as `cache[@"key"] = object;`.
@param object An object to be assigned for the key.
@param key A key to associate with the object. This string will be copied.
*/
- (void)setObject:(id)obj forKeyedSubscript:(NSString *)key;
@end

View File

@@ -5,6 +5,8 @@
#import <Foundation/Foundation.h>
#import "Nullability.h"
#import "PINCacheObjectSubscripting.h"
NS_ASSUME_NONNULL_BEGIN
@class PINDiskCache;
@@ -12,14 +14,23 @@ NS_ASSUME_NONNULL_BEGIN
/**
A callback block which provides only the cache as an argument
*/
typedef void (^PINDiskCacheBlock)(PINDiskCache *cache);
/**
A callback block which provides the cache, key and object as arguments
*/
typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <NSCoding> __nullable object);
/**
A callback block which provides the key and fileURL of the object
*/
typedef void (^PINDiskCacheFileURLBlock)(NSString *key, NSURL * __nullable fileURL);
/**
A callback block which provides a BOOL value as argument
*/
typedef void (^PINDiskCacheContainsBlock)(BOOL containsObject);
typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <NSCoding> __nullable object, NSURL * __nullable fileURL);
/**
`PINDiskCache` is a thread safe key/value store backed by the file system. It accepts any object conforming
@@ -44,7 +55,7 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
<ageLimit> will trigger a GCD timer to periodically to trim the cache with <trimToDate:>.
*/
@interface PINDiskCache : NSObject
@interface PINDiskCache : NSObject <PINCacheObjectSubscripting>
@@ -68,7 +79,7 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
The total number of bytes used on disk, as reported by `NSURLTotalFileAllocatedSizeKey`.
@warning This property should only be read from a call to <synchronouslyLockFileAccessWhileExecutingBlock:> or
its asynchronous equivolent <lockFileAccessWhileExecutingBlock:>
its asynchronous equivalent <lockFileAccessWhileExecutingBlock:>
For example:
@@ -97,6 +108,21 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
*/
@property (assign) NSTimeInterval ageLimit;
/**
The writing protection option used when writing a file on disk. This value is used every time an object is set.
NSDataWritingAtomic and NSDataWritingWithoutOverwriting are ignored if set
Defaults to NSDataWritingFileProtectionNone.
@warning Only new files are affected by the new writing protection. If you need all files to be affected,
you'll have to purge and set the objects back to the cache
Only available on iOS
*/
#if TARGET_OS_IPHONE
@property (assign) NSDataWritingOptions writingProtectionOption;
#endif
/**
If ttlCache is YES, the cache behaves like a ttlCache. This means that once an object enters the
cache, it only lives as long as self.ageLimit. This has the following implications:
@@ -191,12 +217,21 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
*/
- (void)lockFileAccessWhileExecutingBlock:(nullable PINDiskCacheBlock)block;
/**
This method determines whether an object is present for the given key in the cache. This method returns immediately
and executes the passed block after the object is available, potentially in parallel with other blocks on the
<concurrentQueue>.
@see containsObjectForKey:
@param key The key associated with the object.
@param block A block to be executed concurrently after the containment check happened
*/
- (void)containsObjectForKey:(NSString *)key block:(PINDiskCacheContainsBlock)block;
/**
Retrieves the object for the specified key. This method returns immediately and executes the passed
block as soon as the object is available.
@warning The fileURL is only valid for the duration of this block, do not use it after the block ends.
@param key The key associated with the requested object.
@param block A block to be executed serially when the object is available.
*/
@@ -209,10 +244,15 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
@warning Access is protected for the duration of the block, but to maintain safe disk access do not
access this fileURL after the block has ended.
@warning The PINDiskCache lock is held while block is executed. Any synchronous calls to the diskcache
or a cache which owns the instance of the disk cache are likely to cause a deadlock. This is why the block is
*not* passed the instance of the disk cache. You should also avoid doing extensive work while this
lock is held.
@param key The key associated with the requested object.
@param block A block to be executed serially when the file URL is available.
*/
- (void)fileURLForKey:(nullable NSString *)key block:(nullable PINDiskCacheObjectBlock)block;
- (void)fileURLForKey:(NSString *)key block:(nullable PINDiskCacheFileURLBlock)block;
/**
Stores an object in the cache for the specified key. This method returns immediately and executes the
@@ -276,8 +316,14 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
@param block A block to be executed for every object in the cache.
@param completionBlock An optional block to be executed after the enumeration is complete.
@warning The PINDiskCache lock is held while block is executed. Any synchronous calls to the diskcache
or a cache which owns the instance of the disk cache are likely to cause a deadlock. This is why the block is
*not* passed the instance of the disk cache. You should also avoid doing extensive work while this
lock is held.
*/
- (void)enumerateObjectsWithBlock:(PINDiskCacheObjectBlock)block completionBlock:(nullable PINDiskCacheBlock)completionBlock;
- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block completionBlock:(nullable PINDiskCacheBlock)completionBlock;
#pragma mark -
/// @name Synchronous Methods
@@ -292,6 +338,15 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
*/
- (void)synchronouslyLockFileAccessWhileExecutingBlock:(nullable PINDiskCacheBlock)block;
/**
This method determines whether an object is present for the given key in the cache.
@see containsObjectForKey:block:
@param key The key associated with the object.
@result YES if an object is present for the given key in the cache, otherwise NO.
*/
- (BOOL)containsObjectForKey:(NSString *)key;
/**
Retrieves the object for the specified key. This method blocks the calling thread until the
object is available.
@@ -311,7 +366,7 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
@param key The key associated with the object.
@result The file URL for the specified key.
*/
- (NSURL *)fileURLForKey:(nullable NSString *)key;
- (nullable NSURL *)fileURLForKey:(nullable NSString *)key;
/**
Stores an object in the cache for the specified key. This method blocks the calling thread until
@@ -363,10 +418,17 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id <
read from disk, the `object` parameter of the block will be `nil` but the `fileURL` will be available.
This method blocks the calling thread until all objects have been enumerated.
@param block A block to be executed for every object in the cache.
@warning Do not call this method within the event blocks (<didRemoveObjectBlock>, etc.)
Instead use the asynchronous version, <enumerateObjectsWithBlock:completionBlock:>.
@warning The PINDiskCache lock is held while block is executed. Any synchronous calls to the diskcache
or a cache which owns the instance of the disk cache are likely to cause a deadlock. This is why the block is
*not* passed the instance of the disk cache. You should also avoid doing extensive work while this
lock is held.
*/
- (void)enumerateObjectsWithBlock:(nullable PINDiskCacheObjectBlock)block;
- (void)enumerateObjectsWithBlock:(nullable PINDiskCacheFileURLBlock)block;
@end

View File

@@ -8,6 +8,8 @@
#import <UIKit/UIKit.h>
#endif
#import <pthread.h>
#define PINDiskCacheError(error) if (error) { NSLog(@"%@ (%d) ERROR: %@", \
[[NSString stringWithUTF8String:__FILE__] lastPathComponent], \
__LINE__, [error localizedDescription]); }
@@ -15,24 +17,21 @@ __LINE__, [error localizedDescription]); }
static NSString * const PINDiskCachePrefix = @"com.pinterest.PINDiskCache";
static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
@interface PINBackgroundTask : NSObject
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 && !TARGET_OS_WATCH
@property (atomic, assign) UIBackgroundTaskIdentifier taskID;
#endif
+ (instancetype)start;
- (void)end;
@end
typedef NS_ENUM(NSUInteger, PINDiskCacheCondition) {
PINDiskCacheConditionNotReady = 0,
PINDiskCacheConditionReady = 1,
};
@interface PINDiskCache ()
@interface PINDiskCache () {
NSConditionLock *_instanceLock;
}
@property (assign) NSUInteger byteCount;
@property (strong, nonatomic) NSURL *cacheURL;
#if OS_OBJECT_USE_OBJC
@property (strong, nonatomic) dispatch_queue_t asyncQueue;
@property (strong, nonatomic) dispatch_semaphore_t lockSemaphore;
#else
@property (assign, nonatomic) dispatch_queue_t asyncQueue;
@property (assign, nonatomic) dispatch_semaphore_t lockSemaphore;
#endif
@property (strong, nonatomic) NSMutableDictionary *dates;
@property (strong, nonatomic) NSMutableDictionary *sizes;
@@ -50,12 +49,15 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
@synthesize ageLimit = _ageLimit;
@synthesize ttlCache = _ttlCache;
#if TARGET_OS_IPHONE
@synthesize writingProtectionOption = _writingProtectionOption;
#endif
#pragma mark - Initialization -
- (void)dealloc
{
#if !OS_OBJECT_USE_OBJC
dispatch_release(_lockSemaphore);
dispatch_release(_asyncQueue);
_asyncQueue = nil;
#endif
@@ -80,7 +82,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
if (self = [super init]) {
_name = [name copy];
_asyncQueue = dispatch_queue_create([[NSString stringWithFormat:@"%@ Asynchronous Queue", PINDiskCachePrefix] UTF8String], DISPATCH_QUEUE_CONCURRENT);
_lockSemaphore = dispatch_semaphore_create(1);
_instanceLock = [[NSConditionLock alloc] initWithCondition:PINDiskCacheConditionNotReady];
_willAddObjectBlock = nil;
_willRemoveObjectBlock = nil;
_willRemoveAllObjectsBlock = nil;
@@ -91,7 +93,11 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
_byteCount = 0;
_byteLimit = 0;
_ageLimit = 0.0;
#if TARGET_OS_IPHONE
_writingProtectionOption = NSDataWritingFileProtectionNone;
#endif
_dates = [[NSMutableDictionary alloc] init];
_sizes = [[NSMutableDictionary alloc] init];
@@ -99,13 +105,12 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
_cacheURL = [NSURL fileURLWithPathComponents:@[ rootPath, pathComponent ]];
//we don't want to do anything without setting up the disk cache, but we also don't want to block init, it can take a while to initialize
//this is only safe because we use a dispatch_semaphore as a lock. If we switch to an NSLock or posix locks, this will *no longer be safe*.
[self lock];
dispatch_async(_asyncQueue, ^{
[self createCacheDirectory];
[self initializeDiskProperties];
[self unlock];
//should always be able to aquire the lock unless the below code is running.
[_instanceLock lockWhenCondition:PINDiskCacheConditionNotReady];
[self _locked_createCacheDirectory];
[self _locked_initializeDiskProperties];
[_instanceLock unlockWithCondition:PINDiskCacheConditionReady];
});
}
return self;
@@ -113,7 +118,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
- (NSString *)description
{
return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINDiskCachePrefix, _name, self];
return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINDiskCachePrefix, _name, (void *)self];
}
+ (instancetype)sharedCache
@@ -130,7 +135,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
#pragma mark - Private Methods -
- (NSURL *)encodedFileURLForKey:(NSString *)key
- (NSURL *)_locked_encodedFileURLForKey:(NSString *)key
{
if (![key length])
return nil;
@@ -243,8 +248,6 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
+ (void)emptyTrash
{
PINBackgroundTask *task = [PINBackgroundTask start];
dispatch_async([self sharedTrashQueue], ^{
NSError *searchTrashedItemsError = nil;
NSArray *trashedItems = [[NSFileManager defaultManager] contentsOfDirectoryAtURL:[self sharedTrashURL]
@@ -258,14 +261,12 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[[NSFileManager defaultManager] removeItemAtURL:trashedItemURL error:&removeTrashedItemError];
PINDiskCacheError(removeTrashedItemError);
}
[task end];
});
}
#pragma mark - Private Queue Methods -
- (BOOL)createCacheDirectory
- (BOOL)_locked_createCacheDirectory
{
if ([[NSFileManager defaultManager] fileExistsAtPath:[_cacheURL path]])
return NO;
@@ -280,7 +281,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
return success;
}
- (void)initializeDiskProperties
- (void)_locked_initializeDiskProperties
{
NSUInteger byteCount = 0;
NSArray *keys = @[ NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey ];
@@ -314,7 +315,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
self.byteCount = byteCount; // atomic
}
- (BOOL)setFileModificationDate:(NSDate *)date forURL:(NSURL *)fileURL
- (BOOL)_locked_setFileModificationDate:(NSDate *)date forURL:(NSURL *)fileURL
{
if (!date || !fileURL) {
return NO;
@@ -338,77 +339,112 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
- (BOOL)removeFileAndExecuteBlocksForKey:(NSString *)key
{
NSURL *fileURL = [self encodedFileURLForKey:key];
if (!fileURL || ![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]])
return NO;
[self lock];
NSURL *fileURL = [self _locked_encodedFileURLForKey:key];
if (_willRemoveObjectBlock)
_willRemoveObjectBlock(self, key, nil, fileURL);
if (!fileURL || ![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
[self unlock];
return NO;
}
BOOL trashed = [PINDiskCache moveItemAtURLToTrash:fileURL];
if (!trashed)
return NO;
PINDiskCacheObjectBlock willRemoveObjectBlock = _willRemoveObjectBlock;
if (willRemoveObjectBlock) {
[self unlock];
willRemoveObjectBlock(self, key, nil);
[self lock];
}
BOOL trashed = [PINDiskCache moveItemAtURLToTrash:fileURL];
if (!trashed) {
[self unlock];
return NO;
}
[PINDiskCache emptyTrash];
[PINDiskCache emptyTrash];
NSNumber *byteSize = [_sizes objectForKey:key];
if (byteSize)
self.byteCount = _byteCount - [byteSize unsignedIntegerValue]; // atomic
[_sizes removeObjectForKey:key];
[_dates removeObjectForKey:key];
NSNumber *byteSize = [_sizes objectForKey:key];
if (byteSize)
self.byteCount = _byteCount - [byteSize unsignedIntegerValue]; // atomic
PINDiskCacheObjectBlock didRemoveObjectBlock = _didRemoveObjectBlock;
if (didRemoveObjectBlock) {
[self unlock];
_didRemoveObjectBlock(self, key, nil);
[self lock];
}
[_sizes removeObjectForKey:key];
[_dates removeObjectForKey:key];
if (_didRemoveObjectBlock)
_didRemoveObjectBlock(self, key, nil, fileURL);
[self unlock];
return YES;
}
- (void)trimDiskToSize:(NSUInteger)trimByteCount
{
if (_byteCount <= trimByteCount)
return;
NSArray *keysSortedBySize = [_sizes keysSortedByValueUsingSelector:@selector(compare:)];
for (NSString *key in [keysSortedBySize reverseObjectEnumerator]) { // largest objects first
[self removeFileAndExecuteBlocksForKey:key];
if (_byteCount <= trimByteCount)
break;
}
[self lock];
if (_byteCount > trimByteCount) {
NSArray *keysSortedBySize = [_sizes keysSortedByValueUsingSelector:@selector(compare:)];
for (NSString *key in [keysSortedBySize reverseObjectEnumerator]) { // largest objects first
[self unlock];
//unlock, removeFileAndExecuteBlocksForKey handles locking itself
[self removeFileAndExecuteBlocksForKey:key];
[self lock];
if (_byteCount <= trimByteCount)
break;
}
}
[self unlock];
}
- (void)trimDiskToSizeByDate:(NSUInteger)trimByteCount
{
if (_byteCount <= trimByteCount)
return;
NSArray *keysSortedByDate = [_dates keysSortedByValueUsingSelector:@selector(compare:)];
for (NSString *key in keysSortedByDate) { // oldest objects first
[self removeFileAndExecuteBlocksForKey:key];
if (_byteCount <= trimByteCount)
break;
}
[self lock];
if (_byteCount > trimByteCount) {
NSArray *keysSortedByDate = [_dates keysSortedByValueUsingSelector:@selector(compare:)];
for (NSString *key in keysSortedByDate) { // oldest objects first
[self unlock];
//unlock, removeFileAndExecuteBlocksForKey handles locking itself
[self removeFileAndExecuteBlocksForKey:key];
[self lock];
if (_byteCount <= trimByteCount)
break;
}
}
[self unlock];
}
- (void)trimDiskToDate:(NSDate *)trimDate
{
NSArray *keysSortedByDate = [_dates keysSortedByValueUsingSelector:@selector(compare:)];
for (NSString *key in keysSortedByDate) { // oldest files first
NSDate *accessDate = [_dates objectForKey:key];
if (!accessDate)
continue;
[self lock];
NSArray *keysSortedByDate = [_dates keysSortedByValueUsingSelector:@selector(compare:)];
if ([accessDate compare:trimDate] == NSOrderedAscending) { // older than trim date
[self removeFileAndExecuteBlocksForKey:key];
} else {
break;
for (NSString *key in keysSortedByDate) { // oldest files first
NSDate *accessDate = [_dates objectForKey:key];
if (!accessDate)
continue;
if ([accessDate compare:trimDate] == NSOrderedAscending) { // older than trim date
[self unlock];
//unlock, removeFileAndExecuteBlocksForKey handles locking itself
[self removeFileAndExecuteBlocksForKey:key];
[self lock];
} else {
break;
}
}
}
[self unlock];
}
- (void)trimToAgeLimitRecursively
@@ -419,10 +455,8 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
if (ageLimit == 0.0)
return;
[self lock];
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:-ageLimit];
[self trimDiskToDate:date];
[self unlock];
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:-ageLimit];
[self trimDiskToDate:date];
__weak PINDiskCache *weakSelf = self;
@@ -449,6 +483,19 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
});
}
- (void)containsObjectForKey:(NSString *)key block:(PINDiskCacheContainsBlock)block
{
if (!key || !block)
return;
__weak PINDiskCache *weakSelf = self;
dispatch_async(_asyncQueue, ^{
PINDiskCache *strongSelf = weakSelf;
block([strongSelf containsObjectForKey:key]);
});
}
- (void)objectForKey:(NSString *)key block:(PINDiskCacheObjectBlock)block
{
__weak PINDiskCache *weakSelf = self;
@@ -459,14 +506,12 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
id <NSCoding> object = [strongSelf objectForKey:key fileURL:&fileURL];
if (block) {
[strongSelf lock];
block(strongSelf, key, object, fileURL);
[strongSelf unlock];
block(strongSelf, key, object);
}
});
}
- (void)fileURLForKey:(NSString *)key block:(PINDiskCacheObjectBlock)block
- (void)fileURLForKey:(NSString *)key block:(PINDiskCacheFileURLBlock)block
{
__weak PINDiskCache *weakSelf = self;
@@ -476,7 +521,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
if (block) {
[strongSelf lock];
block(strongSelf, key, nil, fileURL);
block(key, fileURL);
[strongSelf unlock];
}
});
@@ -492,9 +537,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf setObject:object forKey:key fileURL:&fileURL];
if (block) {
[strongSelf lock];
block(strongSelf, key, object, fileURL);
[strongSelf unlock];
block(strongSelf, key, object);
}
});
}
@@ -509,9 +552,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf removeObjectForKey:key fileURL:&fileURL];
if (block) {
[strongSelf lock];
block(strongSelf, key, nil, fileURL);
[strongSelf unlock];
block(strongSelf, key, nil);
}
});
}
@@ -525,9 +566,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf trimToSize:trimByteCount];
if (block) {
[strongSelf lock];
block(strongSelf);
[strongSelf unlock];
block(strongSelf);
}
});
}
@@ -541,9 +580,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf trimToDate:trimDate];
if (block) {
[strongSelf lock];
block(strongSelf);
[strongSelf unlock];
block(strongSelf);
}
});
}
@@ -557,9 +594,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf trimToSizeByDate:trimByteCount];
if (block) {
[strongSelf lock];
block(strongSelf);
[strongSelf unlock];
block(strongSelf);
}
});
}
@@ -573,14 +608,12 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf removeAllObjects];
if (block) {
[strongSelf lock];
block(strongSelf);
[strongSelf unlock];
block(strongSelf);
}
});
}
- (void)enumerateObjectsWithBlock:(PINDiskCacheObjectBlock)block completionBlock:(PINDiskCacheBlock)completionBlock
- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block completionBlock:(PINDiskCacheBlock)completionBlock
{
__weak PINDiskCache *weakSelf = self;
@@ -589,9 +622,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[strongSelf enumerateObjectsWithBlock:block];
if (completionBlock) {
[self lock];
completionBlock(strongSelf);
[self unlock];
completionBlock(strongSelf);
}
});
}
@@ -602,16 +633,26 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
{
if (block) {
[self lock];
block(self);
block(self);
[self unlock];
}
}
- (BOOL)containsObjectForKey:(NSString *)key
{
return ([self fileURLForKey:key updateFileModificationDate:NO] != nil);
}
- (__nullable id<NSCoding>)objectForKey:(NSString *)key
{
return [self objectForKey:key fileURL:nil];
}
- (id)objectForKeyedSubscript:(NSString *)key
{
return [self objectForKey:key];
}
- (__nullable id <NSCoding>)objectForKey:(NSString *)key fileURL:(NSURL **)outFileURL
{
NSDate *now = [[NSDate alloc] init];
@@ -623,7 +664,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
NSURL *fileURL = nil;
[self lock];
fileURL = [self encodedFileURLForKey:key];
fileURL = [self _locked_encodedFileURLForKey:key];
object = nil;
if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]] &&
@@ -638,7 +679,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
PINDiskCacheError(error);
}
if (!self->_ttlCache) {
[self setFileModificationDate:now forURL:fileURL];
[self _locked_setFileModificationDate:now forURL:fileURL];
}
}
[self unlock];
@@ -650,22 +691,28 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
return object;
}
/// Helper function to call fileURLForKey:updateFileModificationDate:
- (NSURL *)fileURLForKey:(NSString *)key
{
NSDate *now = [[NSDate alloc] init];
if (!key)
// Don't update the file modification time, if self is a ttlCache
return [self fileURLForKey:key updateFileModificationDate:!self->_ttlCache];
}
- (NSURL *)fileURLForKey:(NSString *)key updateFileModificationDate:(BOOL)updateFileModificationDate
{
if (!key) {
return nil;
}
NSDate *now = [[NSDate alloc] init];
NSURL *fileURL = nil;
[self lock];
fileURL = [self encodedFileURLForKey:key];
fileURL = [self _locked_encodedFileURLForKey:key];
if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) {
// Don't update the file modification time, if self is a ttlCache
if (!self->_ttlCache) {
[self setFileModificationDate:now forURL:fileURL];
if (updateFileModificationDate) {
[self _locked_setFileModificationDate:now forURL:fileURL];
}
} else {
fileURL = nil;
@@ -679,6 +726,11 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
[self setObject:object forKey:key fileURL:nil];
}
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key
{
[self setObject:object forKey:key];
}
- (void)setObject:(id <NSCoding>)object forKey:(NSString *)key fileURL:(NSURL **)outFileURL
{
NSDate *now = [[NSDate alloc] init];
@@ -686,20 +738,32 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
if (!key || !object)
return;
PINBackgroundTask *task = [PINBackgroundTask start];
#if TARGET_OS_IPHONE
NSDataWritingOptions writeOptions = NSDataWritingAtomic | self.writingProtectionOption;
#else
NSDataWritingOptions writeOptions = NSDataWritingAtomic;
#endif
NSURL *fileURL = nil;
[self lock];
fileURL = [self encodedFileURLForKey:key];
if (self->_willAddObjectBlock)
self->_willAddObjectBlock(self, key, object, fileURL);
BOOL written = [NSKeyedArchiver archiveRootObject:object toFile:[fileURL path]];
fileURL = [self _locked_encodedFileURLForKey:key];
PINDiskCacheObjectBlock willAddObjectBlock = self->_willAddObjectBlock;
if (willAddObjectBlock) {
[self unlock];
willAddObjectBlock(self, key, object);
[self lock];
}
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:object];
NSError *writeError = nil;
BOOL written = [data writeToURL:fileURL options:writeOptions error:&writeError];
PINDiskCacheError(writeError);
if (written) {
[self setFileModificationDate:now forURL:fileURL];
[self _locked_setFileModificationDate:now forURL:fileURL];
NSError *error = nil;
NSDictionary *values = [fileURL resourceValuesForKeys:@[ NSURLTotalFileAllocatedSizeKey ] error:&error];
@@ -720,16 +784,18 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
} else {
fileURL = nil;
}
if (self->_didAddObjectBlock)
self->_didAddObjectBlock(self, key, object, written ? fileURL : nil);
PINDiskCacheObjectBlock didAddObjectBlock = self->_didAddObjectBlock;
if (didAddObjectBlock) {
[self unlock];
didAddObjectBlock(self, key, object);
[self lock];
}
[self unlock];
if (outFileURL) {
*outFileURL = fileURL;
}
[task end];
}
- (void)removeObjectForKey:(NSString *)key
@@ -742,16 +808,13 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
if (!key)
return;
PINBackgroundTask *task = [PINBackgroundTask start];
NSURL *fileURL = nil;
[self lock];
fileURL = [self encodedFileURLForKey:key];
[self removeFileAndExecuteBlocksForKey:key];
fileURL = [self _locked_encodedFileURLForKey:key];
[self unlock];
[task end];
[self removeFileAndExecuteBlocksForKey:key];
if (outFileURL) {
*outFileURL = fileURL;
@@ -765,13 +828,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
return;
}
PINBackgroundTask *task = [PINBackgroundTask start];
[self lock];
[self trimDiskToSize:trimByteCount];
[self unlock];
[task end];
[self trimDiskToSize:trimByteCount];
}
- (void)trimToDate:(NSDate *)trimDate
@@ -784,13 +841,7 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
return;
}
PINBackgroundTask *task = [PINBackgroundTask start];
[self lock];
[self trimDiskToDate:trimDate];
[self unlock];
[task end];
[self trimDiskToDate:trimDate];
}
- (void)trimToSizeByDate:(NSUInteger)trimByteCount
@@ -800,60 +851,55 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
return;
}
PINBackgroundTask *task = [PINBackgroundTask start];
[self lock];
[self trimDiskToSizeByDate:trimByteCount];
[self unlock];
[task end];
[self trimDiskToSizeByDate:trimByteCount];
}
- (void)removeAllObjects
{
PINBackgroundTask *task = [PINBackgroundTask start];
[self lock];
if (self->_willRemoveAllObjectsBlock)
self->_willRemoveAllObjectsBlock(self);
PINDiskCacheBlock willRemoveAllObjectsBlock = self->_willRemoveAllObjectsBlock;
if (willRemoveAllObjectsBlock) {
[self unlock];
willRemoveAllObjectsBlock(self);
[self lock];
}
[PINDiskCache moveItemAtURLToTrash:self->_cacheURL];
[PINDiskCache emptyTrash];
[self createCacheDirectory];
[self _locked_createCacheDirectory];
[self->_dates removeAllObjects];
[self->_sizes removeAllObjects];
self.byteCount = 0; // atomic
if (self->_didRemoveAllObjectsBlock)
self->_didRemoveAllObjectsBlock(self);
[self unlock];
[task end];
PINDiskCacheBlock didRemoveAllObjectsBlock = self->_didRemoveAllObjectsBlock;
if (didRemoveAllObjectsBlock) {
[self unlock];
didRemoveAllObjectsBlock(self);
[self lock];
}
[self unlock];
}
- (void)enumerateObjectsWithBlock:(PINDiskCacheObjectBlock)block
- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block
{
if (!block)
return;
PINBackgroundTask *task = [PINBackgroundTask start];
[self lock];
NSDate *now = [NSDate date];
NSArray *keysSortedByDate = [self->_dates keysSortedByValueUsingSelector:@selector(compare:)];
for (NSString *key in keysSortedByDate) {
NSURL *fileURL = [self encodedFileURLForKey:key];
NSURL *fileURL = [self _locked_encodedFileURLForKey:key];
// If the cache should behave like a TTL cache, then only fetch the object if there's a valid ageLimit and the object is still alive
if (!self->_ttlCache || self->_ageLimit <= 0 || fabs([[_dates objectForKey:key] timeIntervalSinceDate:now]) < self->_ageLimit) {
block(self, key, nil, fileURL);
block(key, fileURL);
}
}
[self unlock];
[task end];
}
#pragma mark - Public Thread Safe Accessors -
@@ -1034,11 +1080,11 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
return;
[strongSelf lock];
strongSelf->_byteLimit = byteLimit;
strongSelf->_byteLimit = byteLimit;
[strongSelf unlock];
if (byteLimit > 0)
[strongSelf trimDiskToSizeByDate:byteLimit];
[strongSelf unlock];
});
}
@@ -1094,78 +1140,42 @@ static NSString * const PINDiskCacheSharedName = @"PINDiskCacheShared";
});
}
#if TARGET_OS_IPHONE
- (NSDataWritingOptions)writingProtectionOption {
NSDataWritingOptions option;
[self lock];
option = _writingProtectionOption;
[self unlock];
return option;
}
- (void)setWritingProtectionOption:(NSDataWritingOptions)writingProtectionOption {
__weak PINDiskCache *weakSelf = self;
dispatch_async(_asyncQueue, ^{
PINDiskCache *strongSelf = weakSelf;
if (!strongSelf)
return;
NSDataWritingOptions option = NSDataWritingFileProtectionMask & writingProtectionOption;
[strongSelf lock];
strongSelf->_writingProtectionOption = option;
[strongSelf unlock];
});
}
#endif
- (void)lock
{
dispatch_semaphore_wait(_lockSemaphore, DISPATCH_TIME_FOREVER);
[_instanceLock lockWhenCondition:PINDiskCacheConditionReady];
}
- (void)unlock
{
dispatch_semaphore_signal(_lockSemaphore);
}
@end
@implementation PINBackgroundTask
+ (BOOL)isAppExtension {
static BOOL isExtension;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSDictionary *extensionDictionary = [[NSBundle mainBundle] infoDictionary][@"NSExtension"];
isExtension = [extensionDictionary isKindOfClass:[NSDictionary class]];
});
return isExtension;
}
- (instancetype)init
{
if (self = [super init]) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 && !TARGET_OS_WATCH
_taskID = UIBackgroundTaskInvalid;
#endif
}
return self;
}
+ (instancetype)start
{
PINBackgroundTask *task = nil;
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 && !TARGET_OS_WATCH
if ([self.class isAppExtension]) {
return task;
}
task = [[self alloc] init];
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
task.taskID = [sharedApplication beginBackgroundTaskWithExpirationHandler:^{
UIBackgroundTaskIdentifier taskID = task.taskID;
task.taskID = UIBackgroundTaskInvalid;
[sharedApplication endBackgroundTask:taskID];
}];
#endif
return task;
}
- (void)end
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0 && !TARGET_OS_WATCH
if ([self.class isAppExtension]) {
return;
}
UIBackgroundTaskIdentifier taskID = self.taskID;
self.taskID = UIBackgroundTaskInvalid;
UIApplication *sharedApplication = [UIApplication performSelector:@selector(sharedApplication)];
[sharedApplication endBackgroundTask:taskID];
#endif
[_instanceLock unlockWithCondition:PINDiskCacheConditionReady];
}
@end

View File

@@ -5,6 +5,8 @@
#import <Foundation/Foundation.h>
#import "Nullability.h"
#import "PINCacheObjectSubscripting.h"
NS_ASSUME_NONNULL_BEGIN
@class PINMemoryCache;
@@ -12,7 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
/**
A callback block which provides only the cache as an argument
*/
typedef void (^PINMemoryCacheBlock)(PINMemoryCache *cache);
/**
@@ -20,6 +21,12 @@ typedef void (^PINMemoryCacheBlock)(PINMemoryCache *cache);
*/
typedef void (^PINMemoryCacheObjectBlock)(PINMemoryCache *cache, NSString *key, id __nullable object);
/**
A callback block which provides a BOOL value as argument
*/
typedef void (^PINMemoryCacheContainmentBlock)(BOOL containsObject);
/**
`PINMemoryCache` is a fast, thread safe key/value store similar to `NSCache`. On iOS it will clear itself
automatically to reduce memory usage when the app receives a memory warning or goes into the background.
@@ -37,7 +44,7 @@ typedef void (^PINMemoryCacheObjectBlock)(PINMemoryCache *cache, NSString *key,
a memory cache backed by a disk cache.
*/
@interface PINMemoryCache : NSObject
@interface PINMemoryCache : NSObject <PINCacheObjectSubscripting>
#pragma mark -
/// @name Core
@@ -157,6 +164,17 @@ typedef void (^PINMemoryCacheObjectBlock)(PINMemoryCache *cache, NSString *key,
#pragma mark -
/// @name Asynchronous Methods
/**
This method determines whether an object is present for the given key in the cache. This method returns immediately
and executes the passed block after the object is available, potentially in parallel with other blocks on the
<concurrentQueue>.
@see containsObjectForKey:
@param key The key associated with the object.
@param block A block to be executed concurrently after the containment check happened
*/
- (void)containsObjectForKey:(NSString *)key block:(PINMemoryCacheContainmentBlock)block;
/**
Retrieves the object for the specified key. This method returns immediately and executes the passed
block after the object is available, potentially in parallel with other blocks on the <concurrentQueue>.
@@ -248,6 +266,15 @@ typedef void (^PINMemoryCacheObjectBlock)(PINMemoryCache *cache, NSString *key,
#pragma mark -
/// @name Synchronous Methods
/**
This method determines whether an object is present for the given key in the cache.
@see containsObjectForKey:block:
@param key The key associated with the object.
@result YES if an object is present for the given key in the cache, otherwise NO.
*/
- (BOOL)containsObjectForKey:(NSString *)key;
/**
Retrieves the object for the specified key. This method blocks the calling thread until the
object is available.

View File

@@ -55,7 +55,7 @@ static NSString * const PINMemoryCachePrefix = @"com.pinterest.PINMemoryCache";
{
if (self = [super init]) {
_lockSemaphore = dispatch_semaphore_create(1);
NSString *queueName = [[NSString alloc] initWithFormat:@"%@.%p", PINMemoryCachePrefix, self];
NSString *queueName = [[NSString alloc] initWithFormat:@"%@.%p", PINMemoryCachePrefix, (void *)self];
_concurrentQueue = dispatch_queue_create([queueName UTF8String], DISPATCH_QUEUE_CONCURRENT);
_dictionary = [[NSMutableDictionary alloc] init];
@@ -270,6 +270,21 @@ static NSString * const PINMemoryCachePrefix = @"com.pinterest.PINMemoryCache";
#pragma mark - Public Asynchronous Methods -
- (void)containsObjectForKey:(NSString *)key block:(PINMemoryCacheContainmentBlock)block
{
if (!key || !block)
return;
__weak PINMemoryCache *weakSelf = self;
dispatch_async(_concurrentQueue, ^{
PINMemoryCache *strongSelf = weakSelf;
BOOL containsObject = [strongSelf containsObjectForKey:key];
block(containsObject);
});
}
- (void)objectForKey:(NSString *)key block:(PINMemoryCacheObjectBlock)block
{
__weak PINMemoryCache *weakSelf = self;
@@ -381,6 +396,17 @@ static NSString * const PINMemoryCachePrefix = @"com.pinterest.PINMemoryCache";
#pragma mark - Public Synchronous Methods -
- (BOOL)containsObjectForKey:(NSString *)key
{
if (!key)
return NO;
[self lock];
BOOL containsObject = (_dictionary[key] != nil);
[self unlock];
return containsObject;
}
- (__nullable id)objectForKey:(NSString *)key
{
if (!key)
@@ -404,11 +430,21 @@ static NSString * const PINMemoryCachePrefix = @"com.pinterest.PINMemoryCache";
return object;
}
- (id)objectForKeyedSubscript:(NSString *)key
{
return [self objectForKey:key];
}
- (void)setObject:(id)object forKey:(NSString *)key
{
[self setObject:object forKey:key withCost:0];
}
- (void)setObject:(id)object forKeyedSubscript:(NSString *)key
{
[self setObject:object forKey:key];
}
- (void)setObject:(id)object forKey:(NSString *)key withCost:(NSUInteger)cost
{
if (!key || !object)
@@ -424,6 +460,10 @@ static NSString * const PINMemoryCachePrefix = @"com.pinterest.PINMemoryCache";
willAddObjectBlock(self, key, object);
[self lock];
NSNumber* oldCost = _costs[key];
if (oldCost)
_totalCost -= [oldCost unsignedIntegerValue];
_dictionary[key] = object;
_dates[key] = [[NSDate alloc] init];
_costs[key] = @(cost);

View File

@@ -2,6 +2,7 @@
[![CocoaPods](https://img.shields.io/cocoapods/v/PINCache.svg)](http://cocoadocs.org/docsets/PINCache/)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Tavis CI build](https://img.shields.io/travis/pinterest/PINCache.svg?style=flat)](https://travis-ci.org/pinterest/PINCache)
## Fast, non-deadlocking parallel object cache for iOS and OS X.

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = 'AA6EABED2EEE247391AC0B08'
BlueprintIdentifier = 'F65F50CEFEC41C6C57DD53FD'
BlueprintName = 'Pods-PINRemoteImage Tests-PINRemoteImage'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'libPods-PINRemoteImage Tests-PINRemoteImage.a'>

View File

@@ -14,7 +14,7 @@
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = 'C35373FC2E0A691023108168'
BlueprintIdentifier = 'E6B9896E19DB3F7E3116F098'
BlueprintName = 'Pods-PINRemoteImage-PINRemoteImage'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'libPods-PINRemoteImage-PINRemoteImage.a'>