PINCache.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. // PINCache is a modified version of PINCache
  2. // Modifications by Garrett Moon
  3. // Copyright (c) 2015 Pinterest. All rights reserved.
  4. #import "PINCache.h"
  5. #import "PINOperationQueue.h"
  6. #import "PINOperationGroup.h"
  7. static NSString * const PINCachePrefix = @"com.pinterest.PINCache";
  8. static NSString * const PINCacheSharedName = @"PINCacheShared";
  9. @interface PINCache ()
  10. @property (strong, nonatomic) PINOperationQueue *operationQueue;
  11. @end
  12. @implementation PINCache
  13. #pragma mark - Initialization -
  14. - (instancetype)init
  15. {
  16. @throw [NSException exceptionWithName:@"Must initialize with a name" reason:@"PINCache must be initialized with a name. Call initWithName: instead." userInfo:nil];
  17. return [self initWithName:@""];
  18. }
  19. - (instancetype)initWithName:(NSString *)name
  20. {
  21. return [self initWithName:name fileExtension:nil];
  22. }
  23. - (instancetype)initWithName:(NSString *)name fileExtension:(NSString *)fileExtension
  24. {
  25. return [self initWithName:name rootPath:[NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject] fileExtension:fileExtension];
  26. }
  27. - (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath fileExtension:(NSString *)fileExtension
  28. {
  29. return [self initWithName:name rootPath:rootPath serializer:nil deserializer:nil fileExtension:fileExtension];
  30. }
  31. - (instancetype)initWithName:(NSString *)name rootPath:(NSString *)rootPath serializer:(PINDiskCacheSerializerBlock)serializer deserializer:(PINDiskCacheDeserializerBlock)deserializer fileExtension:(NSString *)fileExtension
  32. {
  33. if (!name)
  34. return nil;
  35. if (self = [super init]) {
  36. _name = [name copy];
  37. //10 may actually be a bit high, but currently much of our threads are blocked on empyting the trash. Until we can resolve that, lets bump this up.
  38. _operationQueue = [[PINOperationQueue alloc] initWithMaxConcurrentOperations:10];
  39. _diskCache = [[PINDiskCache alloc] initWithName:_name rootPath:rootPath serializer:serializer deserializer:deserializer fileExtension:fileExtension operationQueue:_operationQueue];
  40. _memoryCache = [[PINMemoryCache alloc] initWithOperationQueue:_operationQueue];
  41. }
  42. return self;
  43. }
  44. - (NSString *)description
  45. {
  46. return [[NSString alloc] initWithFormat:@"%@.%@.%p", PINCachePrefix, _name, (void *)self];
  47. }
  48. + (instancetype)sharedCache
  49. {
  50. static id cache;
  51. static dispatch_once_t predicate;
  52. dispatch_once(&predicate, ^{
  53. cache = [[self alloc] initWithName:PINCacheSharedName];
  54. });
  55. return cache;
  56. }
  57. #pragma mark - Public Asynchronous Methods -
  58. - (void)containsObjectForKey:(NSString *)key block:(PINCacheObjectContainmentBlock)block
  59. {
  60. if (!key || !block) {
  61. return;
  62. }
  63. __weak PINCache *weakSelf = self;
  64. [self.operationQueue addOperation:^{
  65. PINCache *strongSelf = weakSelf;
  66. BOOL containsObject = [strongSelf containsObjectForKey:key];
  67. block(containsObject);
  68. }];
  69. }
  70. #pragma clang diagnostic push
  71. #pragma clang diagnostic ignored "-Wshadow"
  72. - (void)objectForKey:(NSString *)key block:(PINCacheObjectBlock)block
  73. {
  74. if (!key || !block)
  75. return;
  76. __weak PINCache *weakSelf = self;
  77. [self.operationQueue addOperation:^{
  78. PINCache *strongSelf = weakSelf;
  79. if (!strongSelf)
  80. return;
  81. [strongSelf->_memoryCache objectForKey:key block:^(PINMemoryCache *memoryCache, NSString *memoryCacheKey, id memoryCacheObject) {
  82. PINCache *strongSelf = weakSelf;
  83. if (!strongSelf)
  84. return;
  85. if (memoryCacheObject) {
  86. [strongSelf->_diskCache fileURLForKey:memoryCacheKey block:NULL];
  87. [strongSelf->_operationQueue addOperation:^{
  88. PINCache *strongSelf = weakSelf;
  89. if (strongSelf)
  90. block(strongSelf, memoryCacheKey, memoryCacheObject);
  91. }];
  92. } else {
  93. [strongSelf->_diskCache objectForKey:memoryCacheKey block:^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> diskCacheObject) {
  94. PINCache *strongSelf = weakSelf;
  95. if (!strongSelf)
  96. return;
  97. [strongSelf->_memoryCache setObject:diskCacheObject forKey:diskCacheKey block:nil];
  98. [strongSelf->_operationQueue addOperation:^{
  99. PINCache *strongSelf = weakSelf;
  100. if (strongSelf)
  101. block(strongSelf, diskCacheKey, diskCacheObject);
  102. }];
  103. }];
  104. }
  105. }];
  106. }];
  107. }
  108. #pragma clang diagnostic pop
  109. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key block:(PINCacheObjectBlock)block
  110. {
  111. if (!key || !object)
  112. return;
  113. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  114. [group addOperation:^{
  115. [_memoryCache setObject:object forKey:key];
  116. }];
  117. [group addOperation:^{
  118. [_diskCache setObject:object forKey:key];
  119. }];
  120. if (block) {
  121. [group setCompletion:^{
  122. block(self, key, object);
  123. }];
  124. }
  125. [group start];
  126. }
  127. - (void)removeObjectForKey:(NSString *)key block:(PINCacheObjectBlock)block
  128. {
  129. if (!key)
  130. return;
  131. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  132. [group addOperation:^{
  133. [_memoryCache removeObjectForKey:key];
  134. }];
  135. [group addOperation:^{
  136. [_diskCache removeObjectForKey:key];
  137. }];
  138. if (block) {
  139. [group setCompletion:^{
  140. block(self, key, nil);
  141. }];
  142. }
  143. [group start];
  144. }
  145. - (void)removeAllObjects:(PINCacheBlock)block
  146. {
  147. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  148. [group addOperation:^{
  149. [_memoryCache removeAllObjects];
  150. }];
  151. [group addOperation:^{
  152. [_diskCache removeAllObjects];
  153. }];
  154. if (block) {
  155. [group setCompletion:^{
  156. block(self);
  157. }];
  158. }
  159. [group start];
  160. }
  161. - (void)trimToDate:(NSDate *)date block:(PINCacheBlock)block
  162. {
  163. if (!date)
  164. return;
  165. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  166. [group addOperation:^{
  167. [_memoryCache trimToDate:date];
  168. }];
  169. [group addOperation:^{
  170. [_diskCache trimToDate:date];
  171. }];
  172. if (block) {
  173. [group setCompletion:^{
  174. block(self);
  175. }];
  176. }
  177. [group start];
  178. }
  179. #pragma mark - Public Synchronous Accessors -
  180. - (NSUInteger)diskByteCount
  181. {
  182. __block NSUInteger byteCount = 0;
  183. [_diskCache synchronouslyLockFileAccessWhileExecutingBlock:^(PINDiskCache *diskCache) {
  184. byteCount = diskCache.byteCount;
  185. }];
  186. return byteCount;
  187. }
  188. - (BOOL)containsObjectForKey:(NSString *)key
  189. {
  190. if (!key)
  191. return NO;
  192. return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
  193. }
  194. - (__nullable id)objectForKey:(NSString *)key
  195. {
  196. if (!key)
  197. return nil;
  198. __block id object = nil;
  199. object = [_memoryCache objectForKey:key];
  200. if (object) {
  201. // update the access time on disk
  202. [_diskCache fileURLForKey:key block:NULL];
  203. } else {
  204. object = [_diskCache objectForKey:key];
  205. [_memoryCache setObject:object forKey:key];
  206. }
  207. return object;
  208. }
  209. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key
  210. {
  211. if (!key || !object)
  212. return;
  213. [_memoryCache setObject:object forKey:key];
  214. [_diskCache setObject:object forKey:key];
  215. }
  216. - (id)objectForKeyedSubscript:(NSString *)key
  217. {
  218. return [self objectForKey:key];
  219. }
  220. - (void)setObject:(id)obj forKeyedSubscript:(NSString *)key
  221. {
  222. [self setObject:obj forKey:key];
  223. }
  224. - (void)removeObjectForKey:(NSString *)key
  225. {
  226. if (!key)
  227. return;
  228. [_memoryCache removeObjectForKey:key];
  229. [_diskCache removeObjectForKey:key];
  230. }
  231. - (void)trimToDate:(NSDate *)date
  232. {
  233. if (!date)
  234. return;
  235. [_memoryCache trimToDate:date];
  236. [_diskCache trimToDate:date];
  237. }
  238. - (void)removeAllObjects
  239. {
  240. [_memoryCache removeAllObjects];
  241. [_diskCache removeAllObjects];
  242. }
  243. @end