PINCache.m 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  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 <PINOperation/PINOperation.h>
  6. static NSString * const PINCachePrefix = @"com.pinterest.PINCache";
  7. static NSString * const PINCacheSharedName = @"PINCacheShared";
  8. @interface PINCache ()
  9. @property (copy, nonatomic) NSString *name;
  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 prefix:PINDiskCachePrefix 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. + (PINCache *)sharedCache
  49. {
  50. static PINCache *cache;
  51. static dispatch_once_t predicate;
  52. dispatch_once(&predicate, ^{
  53. cache = [[PINCache alloc] initWithName:PINCacheSharedName];
  54. });
  55. return cache;
  56. }
  57. #pragma mark - Public Asynchronous Methods -
  58. - (void)containsObjectForKeyAsync:(NSString *)key completion:(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)objectForKeyAsync:(NSString *)key completion:(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 objectForKeyAsync:key completion:^(PINMemoryCache *memoryCache, NSString *memoryCacheKey, id memoryCacheObject) {
  82. PINCache *strongSelf = weakSelf;
  83. if (!strongSelf)
  84. return;
  85. if (memoryCacheObject) {
  86. // Update file modification date. TODO: make this a separate method?
  87. [strongSelf->_diskCache fileURLForKeyAsync:memoryCacheKey completion:^(NSString * _Nonnull key, NSURL * _Nullable fileURL) {}];
  88. [strongSelf->_operationQueue addOperation:^{
  89. PINCache *strongSelf = weakSelf;
  90. if (strongSelf)
  91. block(strongSelf, memoryCacheKey, memoryCacheObject);
  92. }];
  93. } else {
  94. [strongSelf->_diskCache objectForKeyAsync:memoryCacheKey completion:^(PINDiskCache *diskCache, NSString *diskCacheKey, id <NSCoding> diskCacheObject) {
  95. PINCache *strongSelf = weakSelf;
  96. if (!strongSelf)
  97. return;
  98. [strongSelf->_memoryCache setObjectAsync:diskCacheObject forKey:diskCacheKey completion:nil];
  99. [strongSelf->_operationQueue addOperation:^{
  100. PINCache *strongSelf = weakSelf;
  101. if (strongSelf)
  102. block(strongSelf, diskCacheKey, diskCacheObject);
  103. }];
  104. }];
  105. }
  106. }];
  107. }];
  108. }
  109. #pragma clang diagnostic pop
  110. - (void)setObjectAsync:(id <NSCoding>)object forKey:(NSString *)key completion:(PINCacheObjectBlock)block
  111. {
  112. [self setObjectAsync:object forKey:key withCost:0 completion:block];
  113. }
  114. - (void)setObjectAsync:(id <NSCoding>)object forKey:(NSString *)key withCost:(NSUInteger)cost completion:(PINCacheObjectBlock)block
  115. {
  116. if (!key || !object)
  117. return;
  118. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  119. [group addOperation:^{
  120. [_memoryCache setObject:object forKey:key withCost:cost];
  121. }];
  122. [group addOperation:^{
  123. [_diskCache setObject:object forKey:key];
  124. }];
  125. if (block) {
  126. [group setCompletion:^{
  127. block(self, key, object);
  128. }];
  129. }
  130. [group start];
  131. }
  132. - (void)removeObjectForKeyAsync:(NSString *)key completion:(PINCacheObjectBlock)block
  133. {
  134. if (!key)
  135. return;
  136. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  137. [group addOperation:^{
  138. [_memoryCache removeObjectForKey:key];
  139. }];
  140. [group addOperation:^{
  141. [_diskCache removeObjectForKey:key];
  142. }];
  143. if (block) {
  144. [group setCompletion:^{
  145. block(self, key, nil);
  146. }];
  147. }
  148. [group start];
  149. }
  150. - (void)removeAllObjectsAsync:(PINCacheBlock)block
  151. {
  152. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  153. [group addOperation:^{
  154. [_memoryCache removeAllObjects];
  155. }];
  156. [group addOperation:^{
  157. [_diskCache removeAllObjects];
  158. }];
  159. if (block) {
  160. [group setCompletion:^{
  161. block(self);
  162. }];
  163. }
  164. [group start];
  165. }
  166. - (void)trimToDateAsync:(NSDate *)date completion:(PINCacheBlock)block
  167. {
  168. if (!date)
  169. return;
  170. PINOperationGroup *group = [PINOperationGroup asyncOperationGroupWithQueue:_operationQueue];
  171. [group addOperation:^{
  172. [_memoryCache trimToDate:date];
  173. }];
  174. [group addOperation:^{
  175. [_diskCache trimToDate:date];
  176. }];
  177. if (block) {
  178. [group setCompletion:^{
  179. block(self);
  180. }];
  181. }
  182. [group start];
  183. }
  184. #pragma mark - Public Synchronous Accessors -
  185. - (NSUInteger)diskByteCount
  186. {
  187. __block NSUInteger byteCount = 0;
  188. [_diskCache synchronouslyLockFileAccessWhileExecutingBlock:^(PINDiskCache *diskCache) {
  189. byteCount = diskCache.byteCount;
  190. }];
  191. return byteCount;
  192. }
  193. - (BOOL)containsObjectForKey:(NSString *)key
  194. {
  195. if (!key)
  196. return NO;
  197. return [_memoryCache containsObjectForKey:key] || [_diskCache containsObjectForKey:key];
  198. }
  199. - (nullable id)objectForKey:(NSString *)key
  200. {
  201. if (!key)
  202. return nil;
  203. __block id object = nil;
  204. object = [_memoryCache objectForKey:key];
  205. if (object) {
  206. // Update file modification date. TODO: make this a separate method?
  207. [_diskCache fileURLForKeyAsync:key completion:^(NSString * _Nonnull key, NSURL * _Nullable fileURL) {}];
  208. } else {
  209. object = [_diskCache objectForKey:key];
  210. [_memoryCache setObject:object forKey:key];
  211. }
  212. return object;
  213. }
  214. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key
  215. {
  216. [self setObject:object forKey:key withCost:0];
  217. }
  218. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key withCost:(NSUInteger)cost
  219. {
  220. if (!key || !object)
  221. return;
  222. [_memoryCache setObject:object forKey:key withCost:cost];
  223. [_diskCache setObject:object forKey:key];
  224. }
  225. - (nullable id)objectForKeyedSubscript:(NSString *)key
  226. {
  227. return [self objectForKey:key];
  228. }
  229. - (void)setObject:(nullable id)obj forKeyedSubscript:(NSString *)key
  230. {
  231. if (obj == nil) {
  232. [self removeObjectForKey:key];
  233. } else {
  234. [self setObject:obj forKey:key];
  235. }
  236. }
  237. - (void)removeObjectForKey:(NSString *)key
  238. {
  239. if (!key)
  240. return;
  241. [_memoryCache removeObjectForKey:key];
  242. [_diskCache removeObjectForKey:key];
  243. }
  244. - (void)trimToDate:(NSDate *)date
  245. {
  246. if (!date)
  247. return;
  248. [_memoryCache trimToDate:date];
  249. [_diskCache trimToDate:date];
  250. }
  251. - (void)removeAllObjects
  252. {
  253. [_memoryCache removeAllObjects];
  254. [_diskCache removeAllObjects];
  255. }
  256. @end
  257. @implementation PINCache (Deprecated)
  258. - (void)containsObjectForKey:(NSString *)key block:(PINCacheObjectContainmentBlock)block
  259. {
  260. [self containsObjectForKeyAsync:key completion:block];
  261. }
  262. - (void)objectForKey:(NSString *)key block:(PINCacheObjectBlock)block
  263. {
  264. [self objectForKeyAsync:key completion:block];
  265. }
  266. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key block:(nullable PINCacheObjectBlock)block
  267. {
  268. [self setObjectAsync:object forKey:key completion:block];
  269. }
  270. - (void)setObject:(id <NSCoding>)object forKey:(NSString *)key withCost:(NSUInteger)cost block:(nullable PINCacheObjectBlock)block
  271. {
  272. [self setObjectAsync:object forKey:key withCost:cost completion:block];
  273. }
  274. - (void)removeObjectForKey:(NSString *)key block:(nullable PINCacheObjectBlock)block
  275. {
  276. [self removeObjectForKeyAsync:key completion:block];
  277. }
  278. - (void)trimToDate:(NSDate *)date block:(nullable PINCacheBlock)block
  279. {
  280. [self trimToDateAsync:date completion:block];
  281. }
  282. - (void)removeAllObjects:(nullable PINCacheBlock)block
  283. {
  284. [self removeAllObjectsAsync:block];
  285. }
  286. @end