Fixed image test flakiness by tying decoded image cache to module lifecycle

Summary: The decoded image cache was previously static, meaning that cached images could persist beyond the lifetime of the module. This resulted in some flakiness in the RCTImageLoaderTests due to the loader returning cached image instanced from previous tests instead of the correct instance.

Reviewed By: bestander

Differential Revision: D3346329

fbshipit-source-id: 375af8894cef1c5b6303c6cdfd7eb57ebcfe3251
This commit is contained in:
Nick Lockwood 2016-05-25 06:50:16 -07:00 committed by Facebook Github Bot 4
parent 57e9df4af2
commit b49633e020
1 changed files with 16 additions and 23 deletions

View File

@ -25,30 +25,10 @@ static NSString *const RCTErrorPrefetchFailure = @"E_PREFETCH_FAILURE";
static const NSUInteger RCTMaxCachableDecodedImageSizeInBytes = 1048576; // 1MB
static NSCache *RCTGetDecodedImageCache(void)
{
static NSCache *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [NSCache new];
cache.totalCostLimit = 5 * 1024 * 1024; // 5MB
// Clear cache in the event of a memory warning, or if app enters background
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidReceiveMemoryWarningNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
[cache removeAllObjects];
}];
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillResignActiveNotification object:nil queue:nil usingBlock:^(__unused NSNotification *note) {
[cache removeAllObjects];
}];
});
return cache;
}
static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size,
CGFloat scale, RCTResizeMode resizeMode)
{
return [NSString stringWithFormat:@"%@|%f|%f|%f|%zd",
return [NSString stringWithFormat:@"%@|%g|%g|%g|%zd",
imageTag, size.width, size.height, scale, resizeMode];
}
@ -73,6 +53,7 @@ static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size,
NSOperationQueue *_imageDecodeQueue;
dispatch_queue_t _URLCacheQueue;
NSURLCache *_URLCache;
NSCache *_decodedImageCache;
NSMutableArray *_pendingTasks;
NSInteger _activeTasks;
NSMutableArray *_pendingDecodes;
@ -92,6 +73,18 @@ RCT_EXPORT_MODULE()
_maxConcurrentDecodingBytes = _maxConcurrentDecodingBytes ?: 30 * 1024 *1024; // 30MB
_URLCacheQueue = dispatch_queue_create("com.facebook.react.ImageLoaderURLCacheQueue", DISPATCH_QUEUE_SERIAL);
_decodedImageCache = [NSCache new];
_decodedImageCache.totalCostLimit = 5 * 1024 * 1024; // 5MB
// Clear cache in the event of a memory warning, or if app enters background
[[NSNotificationCenter defaultCenter] addObserver:_decodedImageCache selector:@selector(removeAllObjects) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:_decodedImageCache selector:@selector(removeAllObjects) name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:_decodedImageCache];
}
- (id<RCTImageURLLoader>)imageURLLoaderForURL:(NSURL *)URL
@ -508,7 +501,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
// Check decoded image cache
NSString *cacheKey = RCTCacheKeyForImage(imageTag, size, scale, resizeMode);
{
UIImage *image = [RCTGetDecodedImageCache() objectForKey:cacheKey];
UIImage *image = [_decodedImageCache objectForKey:cacheKey];
if (image) {
// Most loaders do not return on the main thread, so caller is probably not
// expecting it, and may do expensive post-processing in the callback
@ -523,7 +516,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
if (image) {
CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * 4;
if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) {
[RCTGetDecodedImageCache() setObject:image forKey:cacheKey cost:bytes];
[_decodedImageCache setObject:image forKey:cacheKey cost:bytes];
}
}
completionBlock(error, image);