forked from pinterest/PINCache
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PINDiskCache.h
515 lines (405 loc) · 21.7 KB
/
PINDiskCache.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
// PINCache is a modified version of TMCache
// Modifications by Garrett Moon
// Copyright (c) 2015 Pinterest. All rights reserved.
#import <Foundation/Foundation.h>
#import <PINCache/PINCacheMacros.h>
#import <PINCache/PINCaching.h>
#import <PINCache/PINCacheObjectSubscripting.h>
NS_ASSUME_NONNULL_BEGIN
@class PINDiskCache;
@class PINOperationQueue;
extern NSString * const PINDiskCachePrefix;
/**
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 used for enumeration which provides the key and fileURL of the object plus a stop flag that
may be flipped by the caller.
*/
typedef void (^PINDiskCacheFileURLEnumerationBlock)(NSString *key, NSURL * _Nullable fileURL, BOOL *stop);
/**
A callback block which provides a BOOL value as argument
*/
typedef void (^PINDiskCacheContainsBlock)(BOOL containsObject);
/**
* A block used to serialize object before writing to disk
*
* @param object Object to serialize
* @param key The key associated with the object
*
* @return Serialized object representation
*/
typedef NSData* _Nonnull(^PINDiskCacheSerializerBlock)(id<NSCoding> object, NSString *key);
/**
* A block used to deserialize objects
*
* @param data Serialized object data
* @param key The key associated with the object
*
* @return Deserialized object
*/
typedef id<NSCoding> _Nonnull(^PINDiskCacheDeserializerBlock)(NSData* data, NSString *key);
/**
* A block used to encode keys
*
* @param decodedKey Original/decoded key
*
* @return encoded key
*/
typedef NSString *_Nonnull(^PINDiskCacheKeyEncoderBlock)(NSString *decodedKey);
/**
* A block used to decode keys
*
* @param encodedKey An encoded key
*
* @return decoded key
*/
typedef NSString *_Nonnull(^PINDiskCacheKeyDecoderBlock)(NSString *encodedKey);
/**
`PINDiskCache` is a thread safe key/value store backed by the file system. It accepts any object conforming
to the `NSCoding` protocol, which includes the basic Foundation data types and collection classes and also
many UIKit classes, notably `UIImage`. All work is performed on a serial queue shared by all instances in
the app, and archiving is handled by `NSKeyedArchiver`. This is a particular advantage for `UIImage` because
it skips `UIImagePNGRepresentation()` and retains information like scale and orientation.
The designated initializer for `PINDiskCache` is <initWithName:>. The <name> string is used to create a directory
under Library/Caches that scopes disk access for this instance. Multiple instances with the same name are *not*
allowed as they would conflict with each other.
Unless otherwise noted, all properties and methods are safe to access from any thread at any time. All blocks
will cause the queue to wait, making it safe to access and manipulate the actual cache files on disk for the
duration of the block.
Because this cache is bound by disk I/O it can be much slower than <PINMemoryCache>, although values stored in
`PINDiskCache` persist after application relaunch. Using <PINCache> is recommended over using `PINDiskCache`
by itself, as it adds a fast layer of additional memory caching while still writing to disk.
All access to the cache is dated so the that the least-used objects can be trimmed first. Setting an optional
<ageLimit> will trigger a GCD timer to periodically to trim the cache with <trimToDate:>.
*/
PIN_SUBCLASSING_RESTRICTED
@interface PINDiskCache : NSObject <PINCaching, PINCacheObjectSubscripting>
#pragma mark - Class
/**
@param rootPath The path for where the cache should be stored.
@param prefix The prefix for the cache name.
@param name The name of the cache.
@result The full URL of the cache.
*/
+ (NSURL *)cacheURLWithRootPath:(NSString *)rootPath prefix:(NSString *)prefix name:(NSString *)name;
#pragma mark - Properties
/// @name Core
/**
The prefix to the name of this cache, used to create a directory under Library/Caches and also appearing in stack traces.
*/
@property (readonly) NSString *prefix;
/**
The URL of the directory used by this cache, usually `Library/Caches/com.pinterest.PINDiskCache.(name)`
@warning Do not interact with files under this URL except in <lockFileAccessWhileExecutingBlock:> or
<synchronouslyLockFileAccessWhileExecutingBlock:>.
*/
@property (readonly) NSURL *cacheURL;
/**
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 equivalent <lockFileAccessWhileExecutingBlock:>
For example:
// some background thread
__block NSUInteger byteCount = 0;
[_diskCache synchronouslyLockFileAccessWhileExecutingBlock:^(PINDiskCache *diskCache) {
byteCount = diskCache.byteCount;
}];
*/
@property (readonly) NSUInteger byteCount;
/**
The maximum number of bytes allowed on disk. This value is checked every time an object is set, if the written
size exceeds the limit a trim call is queued. Defaults to `0.0`, meaning no practical limit.
*/
@property (assign) NSUInteger byteLimit;
/**
The maximum number of seconds an object is allowed to exist in the cache. Setting this to a value
greater than `0.0` will start a recurring GCD timer with the same period that calls <trimToDate:>.
Setting it back to `0.0` will stop the timer. Defaults to `0.0`, meaning no limit.
*/
@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:
- Accessing an object in the cache does not extend that object's lifetime in the cache
- When attempting to access an object in the cache that has lived longer than self.ageLimit,
the cache will behave as if the object does not exist
*/
@property (nonatomic, assign, getter=isTTLCache) BOOL ttlCache;
#pragma mark - Event Blocks
/// @name Event Blocks
/**
A block to be executed just before an object is added to the cache. The queue waits during execution.
*/
@property (nullable, copy) PINDiskCacheObjectBlock willAddObjectBlock;
/**
A block to be executed just before an object is removed from the cache. The queue waits during execution.
*/
@property (nullable, copy) PINDiskCacheObjectBlock willRemoveObjectBlock;
/**
A block to be executed just before all objects are removed from the cache as a result of <removeAllObjects:>.
The queue waits during execution.
*/
@property (nullable, copy) PINCacheBlock willRemoveAllObjectsBlock;
/**
A block to be executed just after an object is added to the cache. The queue waits during execution.
*/
@property (nullable, copy) PINDiskCacheObjectBlock didAddObjectBlock;
/**
A block to be executed just after an object is removed from the cache. The queue waits during execution.
*/
@property (nullable, copy) PINDiskCacheObjectBlock didRemoveObjectBlock;
/**
A block to be executed just after all objects are removed from the cache as a result of <removeAllObjects:>.
The queue waits during execution.
*/
@property (nullable, copy) PINCacheBlock didRemoveAllObjectsBlock;
#pragma mark - Lifecycle
/// @name Initialization
/**
A shared cache.
@result The shared singleton cache instance.
*/
@property (class, readonly, strong) PINDiskCache *sharedCache;
/**
Empties the trash with `DISPATCH_QUEUE_PRIORITY_BACKGROUND`. Does not use lock.
*/
+ (void)emptyTrash;
- (instancetype)init NS_UNAVAILABLE;
/**
Multiple instances with the same name are allowed and can safely access
the same data on disk thanks to the magic of seriality.
@see name
@param name The name of the cache.
@result A new cache with the specified name.
*/
- (instancetype)initWithName:(nonnull NSString *)name;
/**
Multiple instances with the same name are allowed and can safely access
the same data on disk thanks to the magic of seriality.
@see name
@param name The name of the cache.
@param rootPath The path of the cache.
@result A new cache with the specified name.
*/
- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath;
/**
@see name
@param name The name of the cache.
@param rootPath The path of the cache.
@param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used.
@param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used.
@result A new cache with the specified name.
*/
- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath serializer:(nullable PINDiskCacheSerializerBlock)serializer deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer;
/**
The designated initializer allowing you to override default NSKeyedArchiver/NSKeyedUnarchiver serialization.
@see name
@param name The name of the cache.
@param rootPath The path of the cache.
@param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used.
@param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used.
@param operationQueue A PINOperationQueue to run asynchronous operations
@result A new cache with the specified name.
*/
- (instancetype)initWithName:(nonnull NSString *)name rootPath:(nonnull NSString *)rootPath serializer:(nullable PINDiskCacheSerializerBlock)serializer deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer operationQueue:(nonnull PINOperationQueue *)operationQueue __attribute__((deprecated));
/**
The designated initializer allowing you to override default NSKeyedArchiver/NSKeyedUnarchiver serialization.
@see name
@param name The name of the cache.
@param prefix The prefix for the cache name. Defaults to com.pinterest.PINDiskCache
@param rootPath The path of the cache.
@param serializer A block used to serialize object. If nil provided, default NSKeyedArchiver serialized will be used.
@param deserializer A block used to deserialize object. If nil provided, default NSKeyedUnarchiver serialized will be used.
@param keyEncoder A block used to encode key(filename). If nil provided, default url encoder will be used
@param keyDecoder A block used to decode key(filename). If nil provided, default url decoder will be used
@param operationQueue A PINOperationQueue to run asynchronous operations
@result A new cache with the specified name.
*/
- (instancetype)initWithName:(nonnull NSString *)name
prefix:(nonnull NSString *)prefix
rootPath:(nonnull NSString *)rootPath
serializer:(nullable PINDiskCacheSerializerBlock)serializer
deserializer:(nullable PINDiskCacheDeserializerBlock)deserializer
keyEncoder:(nullable PINDiskCacheKeyEncoderBlock)keyEncoder
keyDecoder:(nullable PINDiskCacheKeyDecoderBlock)keyDecoder
operationQueue:(nonnull PINOperationQueue *)operationQueue NS_DESIGNATED_INITIALIZER;
#pragma mark - Asynchronous Methods
/// @name Asynchronous Methods
/**
Locks access to ivars and allows safe interaction with files on disk. This method returns immediately.
@warning Calling synchronous methods on the diskCache inside this block will likely cause a deadlock.
@param block A block to be executed when a lock is available.
*/
- (void)lockFileAccessWhileExecutingBlockAsync:(PINCacheBlock)block;
/**
Retrieves the object for the specified key. This method returns immediately and executes the passed
block as soon as the object is available.
@param key The key associated with the requested object.
@param block A block to be executed serially when the object is available.
*/
- (void)objectForKeyAsync:(NSString *)key completion:(nullable PINDiskCacheObjectBlock)block;
/**
Retrieves the fileURL for the specified key without actually reading the data from disk. This method
returns immediately and executes the passed block as soon as the object is available.
@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)fileURLForKeyAsync:(NSString *)key completion:(PINDiskCacheFileURLBlock)block;
/**
Stores an object in the cache for the specified key. This method returns immediately and executes the
passed block as soon as the object has been stored.
@param object An object to store in the cache.
@param key A key to associate with the object. This string will be copied.
@param block A block to be executed serially after the object has been stored, or nil.
*/
- (void)setObjectAsync:(id <NSCoding>)object forKey:(NSString *)key completion:(nullable PINDiskCacheObjectBlock)block;
/**
Stores an object in the cache for the specified key and the specified memory cost. If the cost causes the total
to go over the <memoryCache.costLimit> the cache is trimmed (oldest objects first). This method returns immediately
and executes the passed block after the object has been stored, potentially in parallel with other blocks
on the <concurrentQueue>.
@param object An object to store in the cache.
@param key A key to associate with the object. This string will be copied.
@param cost An amount to add to the <memoryCache.totalCost>.
@param block A block to be executed concurrently after the object has been stored, or nil.
*/
- (void)setObjectAsync:(id <NSCoding>)object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(nullable PINCacheObjectBlock)block;
/**
Removes the object for the specified key. This method returns immediately and executes the passed block
as soon as the object has been removed.
@param key The key associated with the object to be removed.
@param block A block to be executed serially after the object has been removed, or nil.
*/
- (void)removeObjectForKeyAsync:(NSString *)key completion:(nullable PINDiskCacheObjectBlock)block;
/**
Removes objects from the cache, largest first, until the cache is equal to or smaller than the specified byteCount.
This method returns immediately and executes the passed block as soon as the cache has been trimmed.
@param byteCount The cache will be trimmed equal to or smaller than this size.
@param block A block to be executed serially after the cache has been trimmed, or nil.
*/
- (void)trimToSizeAsync:(NSUInteger)byteCount completion:(nullable PINCacheBlock)block;
/**
Removes objects from the cache, ordered by date (least recently used first), until the cache is equal to or smaller
than the specified byteCount. This method returns immediately and executes the passed block as soon as the cache has
been trimmed.
@param byteCount The cache will be trimmed equal to or smaller than this size.
@param block A block to be executed serially after the cache has been trimmed, or nil.
*/
- (void)trimToSizeByDateAsync:(NSUInteger)byteCount completion:(nullable PINCacheBlock)block;
/**
Loops through all objects in the cache (reads and writes are suspended during the enumeration). Data is not actually
read from disk, the `object` parameter of the block will be `nil` but the `fileURL` will be available.
This method returns immediately.
@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)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLEnumerationBlock)block completionBlock:(nullable PINCacheBlock)completionBlock;
#pragma mark - Synchronous Methods
/// @name Synchronous Methods
/**
Locks access to ivars and allows safe interaction with files on disk. This method only returns once the block
has been run.
@warning Calling synchronous methods on the diskCache inside this block will likely cause a deadlock.
@param block A block to be executed when a lock is available.
*/
- (void)synchronouslyLockFileAccessWhileExecutingBlock:(PIN_NOESCAPE PINCacheBlock)block;
/**
Retrieves the object for the specified key. This method blocks the calling thread until the
object is available.
@see objectForKeyAsync:completion:
@param key The key associated with the object.
@result The object for the specified key.
*/
- (nullable id <NSCoding>)objectForKey:(NSString *)key;
/**
Retrieves the file URL for the specified key. This method blocks the calling thread until the
url is available. Do not use this URL anywhere except with <lockFileAccessWhileExecutingBlock:>. This method probably
shouldn't even exist, just use the asynchronous one.
@see fileURLForKeyAsync:completion:
@param key The key associated with the object.
@result The file URL for the specified key.
*/
- (nullable NSURL *)fileURLForKey:(nullable NSString *)key;
/**
Stores an object in the cache for the specified key. This method blocks the calling thread until
the object has been stored.
@see setObjectAsync:forKey:completion:
@param object An object to store in the cache.
@param key A key to associate with the object. This string will be copied.
*/
- (void)setObject:(nullable id <NSCoding>)object forKey:(NSString *)key;
/**
Removes objects from the cache, largest first, until the cache is equal to or smaller than the
specified byteCount. This method blocks the calling thread until the cache has been trimmed.
@see trimToSizeAsync:
@param byteCount The cache will be trimmed equal to or smaller than this size.
*/
- (void)trimToSize:(NSUInteger)byteCount;
/**
Removes objects from the cache, ordered by date (least recently used first), until the cache is equal to or
smaller than the specified byteCount. This method blocks the calling thread until the cache has been trimmed.
@see trimToSizeByDateAsync:
@param byteCount The cache will be trimmed equal to or smaller than this size.
*/
- (void)trimToSizeByDate:(NSUInteger)byteCount;
/**
Loops through all objects in the cache (reads and writes are suspended during the enumeration). Data is not actually
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.
@see enumerateObjectsWithBlockAsync:completionBlock
@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:(PIN_NOESCAPE PINDiskCacheFileURLEnumerationBlock)block;
@end
#pragma mark - Deprecated
/**
A callback block which provides only the cache as an argument
*/
typedef void (^PINDiskCacheBlock)(PINDiskCache *cache);
@interface PINDiskCache (Deprecated)
- (void)lockFileAccessWhileExecutingBlock:(nullable PINCacheBlock)block __attribute__((deprecated));
- (void)containsObjectForKey:(NSString *)key block:(PINDiskCacheContainsBlock)block __attribute__((deprecated));
- (void)objectForKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block __attribute__((deprecated));
- (void)fileURLForKey:(NSString *)key block:(nullable PINDiskCacheFileURLBlock)block __attribute__((deprecated));
- (void)setObject:(id <NSCoding>)object forKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block __attribute__((deprecated));
- (void)removeObjectForKey:(NSString *)key block:(nullable PINDiskCacheObjectBlock)block __attribute__((deprecated));
- (void)trimToDate:(NSDate *)date block:(nullable PINDiskCacheBlock)block __attribute__((deprecated));
- (void)trimToSize:(NSUInteger)byteCount block:(nullable PINDiskCacheBlock)block __attribute__((deprecated));
- (void)trimToSizeByDate:(NSUInteger)byteCount block:(nullable PINDiskCacheBlock)block __attribute__((deprecated));
- (void)removeAllObjects:(nullable PINDiskCacheBlock)block __attribute__((deprecated));
- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block completionBlock:(nullable PINDiskCacheBlock)completionBlock __attribute__((deprecated));
@end
NS_ASSUME_NONNULL_END