From 11ee57deec5d1df7568b6cfb9a5e796c2a1a1cfc Mon Sep 17 00:00:00 2001 From: Matt Lazar Date: Tue, 3 Oct 2017 15:23:06 -0400 Subject: [PATCH 1/4] Update enumeration methods to allow a stop flag to be flipped by caller --- Source/PINCaching.h | 6 ++++++ Source/PINDiskCache.h | 10 ++++++++-- Source/PINDiskCache.m | 13 +++++++++---- Source/PINMemoryCache.h | 4 ++-- Source/PINMemoryCache.m | 13 +++++++++---- Tests/PINCacheTests.m | 12 ++++++------ 6 files changed, 40 insertions(+), 18 deletions(-) diff --git a/Source/PINCaching.h b/Source/PINCaching.h index c558751c..6b94136f 100644 --- a/Source/PINCaching.h +++ b/Source/PINCaching.h @@ -24,6 +24,12 @@ typedef void (^PINCacheBlock)(id cache); */ typedef void (^PINCacheObjectBlock)(id cache, NSString *key, id _Nullable object); +/** + A callback block used for enumeration which provides the cache, key and object as arguments plus a stop flag that + may be flipped by the caller. + */ +typedef void (^PINCacheObjectEnumerationBlock)(id cache, NSString *key, id _Nullable object, BOOL *stop); + /** A callback block which provides a BOOL value as argument */ diff --git a/Source/PINDiskCache.h b/Source/PINDiskCache.h index be0883a9..24e1d1b6 100644 --- a/Source/PINDiskCache.h +++ b/Source/PINDiskCache.h @@ -25,6 +25,12 @@ typedef void (^PINDiskCacheObjectBlock)(PINDiskCache *cache, NSString *key, id < */ 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 */ @@ -399,7 +405,7 @@ PIN_SUBCLASSING_RESTRICTED lock is held. */ -- (void)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLBlock)block completionBlock:(nullable PINCacheBlock)completionBlock; +- (void)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLEnumerationBlock)block completionBlock:(nullable PINCacheBlock)completionBlock; #pragma mark - Synchronous Methods /// @name Synchronous Methods @@ -480,7 +486,7 @@ PIN_SUBCLASSING_RESTRICTED lock is held. */ -- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINDiskCacheFileURLBlock)block; +- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINDiskCacheFileURLEnumerationBlock)block; @end diff --git a/Source/PINDiskCache.m b/Source/PINDiskCache.m index bf449db3..41a118af 100644 --- a/Source/PINDiskCache.m +++ b/Source/PINDiskCache.m @@ -797,7 +797,7 @@ - (void)removeAllObjectsAsync:(PINCacheBlock)block } withPriority:PINOperationQueuePriorityLow]; } -- (void)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLBlock)block completionBlock:(PINCacheBlock)completionBlock +- (void)enumerateObjectsWithBlockAsync:(PINDiskCacheFileURLEnumerationBlock)block completionBlock:(PINCacheBlock)completionBlock { __weak PINDiskCache *weakSelf = self; @@ -1093,7 +1093,7 @@ - (void)removeAllObjects [self unlock]; } -- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block +- (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLEnumerationBlock)block { if (!block) return; @@ -1106,7 +1106,10 @@ - (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block NSURL *fileURL = [self 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(key, fileURL); + BOOL stop; + block(key, fileURL, &stop); + if (stop) + break; } } [self unlock]; @@ -1444,7 +1447,9 @@ - (void)removeAllObjects:(nullable PINDiskCacheBlock)block - (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLBlock)block completionBlock:(nullable PINDiskCacheBlock)completionBlock { - [self enumerateObjectsWithBlockAsync:block completionBlock:completionBlock]; + [self enumerateObjectsWithBlockAsync:^(NSString * _Nonnull key, NSURL * _Nullable fileURL, BOOL * _Nonnull stop) { + block(key, fileURL); + } completionBlock:completionBlock]; } @end diff --git a/Source/PINMemoryCache.h b/Source/PINMemoryCache.h index dc350abb..5eee26a9 100644 --- a/Source/PINMemoryCache.h +++ b/Source/PINMemoryCache.h @@ -177,7 +177,7 @@ PIN_SUBCLASSING_RESTRICTED @param block A block to be executed for every object in the cache. @param completionBlock An optional block to be executed concurrently when the enumeration is complete. */ -- (void)enumerateObjectsWithBlockAsync:(PINCacheObjectBlock)block completionBlock:(nullable PINCacheBlock)completionBlock; +- (void)enumerateObjectsWithBlockAsync:(PINCacheObjectEnumerationBlock)block completionBlock:(nullable PINCacheBlock)completionBlock; #pragma mark - Synchronous Methods /// @name Synchronous Methods @@ -212,7 +212,7 @@ PIN_SUBCLASSING_RESTRICTED Instead use the asynchronous version, . */ -- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINCacheObjectBlock)block; +- (void)enumerateObjectsWithBlock:(PIN_NOESCAPE PINCacheObjectEnumerationBlock)block; @end diff --git a/Source/PINMemoryCache.m b/Source/PINMemoryCache.m index 2421d233..f5433ea4 100644 --- a/Source/PINMemoryCache.m +++ b/Source/PINMemoryCache.m @@ -395,7 +395,7 @@ - (void)removeAllObjectsAsync:(PINCacheBlock)block } withPriority:PINOperationQueuePriorityHigh]; } -- (void)enumerateObjectsWithBlockAsync:(PINCacheObjectBlock)block completionBlock:(PINCacheBlock)completionBlock +- (void)enumerateObjectsWithBlockAsync:(PINCacheObjectEnumerationBlock)block completionBlock:(PINCacheBlock)completionBlock { __weak PINMemoryCache *weakSelf = self; @@ -550,7 +550,7 @@ - (void)removeAllObjects } -- (void)enumerateObjectsWithBlock:(PINCacheObjectBlock)block +- (void)enumerateObjectsWithBlock:(PINCacheObjectEnumerationBlock)block { if (!block) return; @@ -562,7 +562,10 @@ - (void)enumerateObjectsWithBlock:(PINCacheObjectBlock)block for (NSString *key in keysSortedByDate) { // 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, _dictionary[key]); + BOOL stop; + block(self, key, _dictionary[key], &stop); + if (stop) + break; } } [self unlock]; @@ -827,7 +830,9 @@ - (void)removeAllObjects:(nullable PINMemoryCacheBlock)block - (void)enumerateObjectsWithBlock:(PINMemoryCacheObjectBlock)block completionBlock:(nullable PINMemoryCacheBlock)completionBlock { - [self enumerateObjectsWithBlockAsync:block completionBlock:completionBlock]; + [self enumerateObjectsWithBlockAsync:^(id _Nonnull cache, NSString * _Nonnull key, id _Nullable object, BOOL * _Nonnull stop) { + block(cache, key, object); + } completionBlock:completionBlock]; } @end diff --git a/Tests/PINCacheTests.m b/Tests/PINCacheTests.m index 2154211c..f689166c 100644 --- a/Tests/PINCacheTests.m +++ b/Tests/PINCacheTests.m @@ -503,7 +503,7 @@ - (void)testMemoryCacheEnumerationWithWarning __block NSUInteger enumCount = 0; self.cache.memoryCache.didReceiveMemoryWarningBlock = ^(PINMemoryCache *cache) { - [cache enumerateObjectsWithBlockAsync:^(PINMemoryCache *cache, NSString *key, id object) { + [cache enumerateObjectsWithBlockAsync:^(PINMemoryCache *cache, NSString *key, id object, BOOL *stop) { @synchronized (self) { enumCount++; } @@ -543,7 +543,7 @@ - (void)testDiskCacheEnumeration __block NSUInteger enumCount = 0; - [self.cache.diskCache enumerateObjectsWithBlockAsync:^(NSString *key, NSURL *fileURL) { + [self.cache.diskCache enumerateObjectsWithBlockAsync:^(NSString *key, NSURL *fileURL, BOOL *stop) { @synchronized (self) { enumCount++; } @@ -759,14 +759,14 @@ - (void)_testTTLCacheObjectEnumeration { // With the TTL cache enabled, we expect enumerating over the caches to yield 0 objects NSUInteger expectedObjCount = 0; __block NSUInteger objCount = 0; - [self.cache.diskCache enumerateObjectsWithBlock:^(NSString * _Nonnull key, NSURL * _Nullable fileURL) { + [self.cache.diskCache enumerateObjectsWithBlock:^(NSString * _Nonnull key, NSURL * _Nullable fileURL, BOOL *stop) { objCount++; }]; XCTAssertEqual(objCount, expectedObjCount, @"Expected %lu objects in the cache", (unsigned long)expectedObjCount); objCount = 0; - [self.cache.memoryCache enumerateObjectsWithBlock:^(PINMemoryCache *cache, NSString *key, id _Nullable object) { + [self.cache.memoryCache enumerateObjectsWithBlock:^(PINMemoryCache *cache, NSString *key, id _Nullable object, BOOL *stop) { objCount++; }]; @@ -785,14 +785,14 @@ - (void)_testTTLCacheObjectEnumeration { // With the TTL cache disabled, we expect enumerating over the caches to yield 1 object each, since the 2nd cache clearing hasn't happened yet expectedObjCount = 1; objCount = 0; - [self.cache.diskCache enumerateObjectsWithBlock:^(NSString * _Nonnull key, NSURL * _Nullable fileURL) { + [self.cache.diskCache enumerateObjectsWithBlock:^(NSString * _Nonnull key, NSURL * _Nullable fileURL, BOOL *stop) { objCount++; }]; XCTAssertEqual(objCount, expectedObjCount, @"Expected %lu objects in the cache", (unsigned long)expectedObjCount); objCount = 0; - [self.cache.memoryCache enumerateObjectsWithBlock:^(PINMemoryCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object) { + [self.cache.memoryCache enumerateObjectsWithBlock:^(PINMemoryCache * _Nonnull cache, NSString * _Nonnull key, id _Nullable object, BOOL *stop) { objCount++; }]; From 1c87c6a41e9d9b34ae15554b0551a629371fa188 Mon Sep 17 00:00:00 2001 From: Matt Lazar Date: Tue, 3 Oct 2017 16:13:54 -0400 Subject: [PATCH 2/4] Fix compiler warning --- Source/PINMemoryCache.m | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/PINMemoryCache.m b/Source/PINMemoryCache.m index f5433ea4..faaf9f15 100644 --- a/Source/PINMemoryCache.m +++ b/Source/PINMemoryCache.m @@ -831,7 +831,10 @@ - (void)removeAllObjects:(nullable PINMemoryCacheBlock)block - (void)enumerateObjectsWithBlock:(PINMemoryCacheObjectBlock)block completionBlock:(nullable PINMemoryCacheBlock)completionBlock { [self enumerateObjectsWithBlockAsync:^(id _Nonnull cache, NSString * _Nonnull key, id _Nullable object, BOOL * _Nonnull stop) { - block(cache, key, object); + if ([cache isKindOfClass:[PINMemoryCache class]]) { + PINMemoryCache *memoryCache = (PINMemoryCache *)cache; + block(memoryCache, key, object); + } } completionBlock:completionBlock]; } From a9c63201889dc50228fd452e20d4e537b5a7969e Mon Sep 17 00:00:00 2001 From: Matt Lazar Date: Tue, 3 Oct 2017 16:17:38 -0400 Subject: [PATCH 3/4] Add CHANGELOG entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cec87e46..5117fd5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Add your own contributions to the next release on the line below this with your name. - [fix] Add some sane limits to the disk cache: [#201]https://github.com/pinterest/PINCache/pull/201 +- [new] Update enumeration methods to allow a stop flag to be flipped by caller: [#204](https://github.com/pinterest/PINCache/pull/204) ## 3.0.1 -- Beta 5 - [fix] Respect small byteLimit settings by checking object size in setObject: [#198](https://github.com/pinterest/PINCache/pull/198) From 9c96f0c5138bb4f15379dfdf3680101e44c2914c Mon Sep 17 00:00:00 2001 From: Matt Lazar Date: Wed, 4 Oct 2017 07:24:59 -0400 Subject: [PATCH 4/4] Changes to address garrettmoon's comments --- Source/PINDiskCache.m | 4 ++-- Source/PINMemoryCache.m | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Source/PINDiskCache.m b/Source/PINDiskCache.m index 41a118af..f9c52cb2 100644 --- a/Source/PINDiskCache.m +++ b/Source/PINDiskCache.m @@ -1106,10 +1106,10 @@ - (void)enumerateObjectsWithBlock:(PINDiskCacheFileURLEnumerationBlock)block NSURL *fileURL = [self 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) { - BOOL stop; + BOOL stop = NO; block(key, fileURL, &stop); if (stop) - break; + break; } } [self unlock]; diff --git a/Source/PINMemoryCache.m b/Source/PINMemoryCache.m index faaf9f15..e23ebc59 100644 --- a/Source/PINMemoryCache.m +++ b/Source/PINMemoryCache.m @@ -562,10 +562,10 @@ - (void)enumerateObjectsWithBlock:(PINCacheObjectEnumerationBlock)block for (NSString *key in keysSortedByDate) { // 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) { - BOOL stop; + BOOL stop = NO; block(self, key, _dictionary[key], &stop); if (stop) - break; + break; } } [self unlock]; @@ -831,10 +831,10 @@ - (void)removeAllObjects:(nullable PINMemoryCacheBlock)block - (void)enumerateObjectsWithBlock:(PINMemoryCacheObjectBlock)block completionBlock:(nullable PINMemoryCacheBlock)completionBlock { [self enumerateObjectsWithBlockAsync:^(id _Nonnull cache, NSString * _Nonnull key, id _Nullable object, BOOL * _Nonnull stop) { - if ([cache isKindOfClass:[PINMemoryCache class]]) { - PINMemoryCache *memoryCache = (PINMemoryCache *)cache; - block(memoryCache, key, object); - } + if ([cache isKindOfClass:[PINMemoryCache class]]) { + PINMemoryCache *memoryCache = (PINMemoryCache *)cache; + block(memoryCache, key, object); + } } completionBlock:completionBlock]; }