mirror of
https://github.com/status-im/react-native.git
synced 2025-01-14 19:44:13 +00:00
Change RCTImageLoader's Cache System to default NSURLRequest's cache system
Summary: Before this PR, ```RCTImageLodaer```'s Cache was too big(200MB on disk) and It doesn't work with HTTP Cache-Control header. So to provide dynamic image, the users must have to add random value on url( ex. adding current date) to avoid cache. So I change that cache system to default ```NSURLRequest```'s cache system, which is well-working with HTTP specs. As the discussion on this issue #7571 , making custom cache policy processor is not ready yet and useless, over-tech things, I think. Even we have no plan about image cache system(or would change plan later), before having a nice plan, I think we should let user use image module with common HTTP Specs. So I remove custom ```NSURLCache```, and make logic like below, 1. try fetch image, 2. on response, get ```Date``` on response's header and make ```cacheKey``` with ```Date```. > (why? because if ```NSURLRequest```'s response was cached, the response's ```Date``` header dosen't change.) 3. find decoded imag Closes https://github.com/facebook/react-native/pull/8235 Reviewed By: bnham Differential Revision: D3469086 Pulled By: javache fbshipit-source-id: 35a5552cda6e6c367481020bbf3c28eb4a9d0207
This commit is contained in:
parent
5903949ad6
commit
631785f2ee
@ -176,6 +176,9 @@ typedef dispatch_block_t RCTImageLoaderCancellationBlock;
|
||||
* Decode an image from the data object. The method should call the
|
||||
* completionHandler when the decoding operation has finished. The method
|
||||
* should also return a cancellation block, if applicable.
|
||||
*
|
||||
* If you provide a custom image decoder, you most implement scheduling yourself,
|
||||
* to avoid decoding large amounts of images at the same time.
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
|
||||
size:(CGSize)size
|
||||
|
@ -22,13 +22,13 @@
|
||||
#import "RCTNetworking.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
static const NSUInteger RCTMaxCachableDecodedImageSizeInBytes = 1048576; // 1MB
|
||||
static const NSUInteger RCTMaxCachableDecodedImageSizeInBytes = 1024 * 1024; // 1MB
|
||||
|
||||
static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size,
|
||||
CGFloat scale, RCTResizeMode resizeMode)
|
||||
static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size, CGFloat scale,
|
||||
RCTResizeMode resizeMode, NSString *responseDate)
|
||||
{
|
||||
return [NSString stringWithFormat:@"%@|%g|%g|%g|%zd",
|
||||
imageTag, size.width, size.height, scale, resizeMode];
|
||||
return [NSString stringWithFormat:@"%@|%g|%g|%g|%zd|%@",
|
||||
imageTag, size.width, size.height, scale, resizeMode, responseDate];
|
||||
}
|
||||
|
||||
@implementation UIImage (React)
|
||||
@ -50,8 +50,7 @@ static NSString *RCTCacheKeyForImage(NSString *imageTag, CGSize size,
|
||||
NSArray<id<RCTImageURLLoader>> *_loaders;
|
||||
NSArray<id<RCTImageDataDecoder>> *_decoders;
|
||||
NSOperationQueue *_imageDecodeQueue;
|
||||
dispatch_queue_t _URLCacheQueue;
|
||||
NSURLCache *_URLCache;
|
||||
dispatch_queue_t _URLRequestQueue;
|
||||
NSCache *_decodedImageCache;
|
||||
NSMutableArray *_pendingTasks;
|
||||
NSInteger _activeTasks;
|
||||
@ -71,7 +70,7 @@ RCT_EXPORT_MODULE()
|
||||
_maxConcurrentDecodingTasks = _maxConcurrentDecodingTasks ?: 2;
|
||||
_maxConcurrentDecodingBytes = _maxConcurrentDecodingBytes ?: 30 * 1024 * 1024; // 30MB
|
||||
|
||||
_URLCacheQueue = dispatch_queue_create("com.facebook.react.ImageLoaderURLCacheQueue", DISPATCH_QUEUE_SERIAL);
|
||||
_URLRequestQueue = dispatch_queue_create("com.facebook.react.ImageLoaderURLRequestQueue", DISPATCH_QUEUE_SERIAL);
|
||||
|
||||
_decodedImageCache = [NSCache new];
|
||||
_decodedImageCache.totalCostLimit = 5 * 1024 * 1024; // 5MB
|
||||
@ -220,7 +219,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)loadImageWithURLRequest:(NSURLRequest *)imageURLRequest
|
||||
callback:(RCTImageLoaderCompletionBlock)callback
|
||||
callback:(RCTImageLoaderCompletionBlock)callback
|
||||
{
|
||||
return [self loadImageWithURLRequest:imageURLRequest
|
||||
size:CGSizeZero
|
||||
@ -233,8 +232,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
|
||||
- (void)dequeueTasks
|
||||
{
|
||||
dispatch_async(_URLCacheQueue, ^{
|
||||
|
||||
dispatch_async(_URLRequestQueue, ^{
|
||||
// Remove completed tasks
|
||||
for (RCTNetworkTask *task in self->_pendingTasks.reverseObjectEnumerator) {
|
||||
switch (task.status) {
|
||||
@ -287,12 +285,12 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
* path taken. This is useful if you want to skip decoding, e.g. when preloading
|
||||
* the image, or retrieving metadata.
|
||||
*/
|
||||
- (RCTImageLoaderCancellationBlock)loadImageOrDataWithURLRequest:(NSURLRequest *)request
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult))completionBlock
|
||||
- (RCTImageLoaderCancellationBlock)_loadImageOrDataWithURLRequest:(NSURLRequest *)request
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(RCTResizeMode)resizeMode
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, NSString *cacheKey))completionBlock
|
||||
{
|
||||
__block volatile uint32_t cancelled = 0;
|
||||
__block dispatch_block_t cancelLoad = nil;
|
||||
@ -310,21 +308,24 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
BOOL requiresScheduling = [loadHandler respondsToSelector:@selector(requiresScheduling)] ?
|
||||
[loadHandler requiresScheduling] : YES;
|
||||
|
||||
void (^completionHandler)(NSError *error, id imageOrData) = ^(NSError *error, id imageOrData) {
|
||||
void (^completionHandler)(NSError *, id, NSString *) = ^(NSError *error, id imageOrData, NSString *fetchDate) {
|
||||
BOOL cacheResult = [loadHandler respondsToSelector:@selector(shouldCacheLoadedImages)] ?
|
||||
[loadHandler shouldCacheLoadedImages] : YES;
|
||||
// If we've received an image, we should try to set it synchronously,
|
||||
// if it's data, do decoding on a background thread.
|
||||
NSString *cacheKey = cacheResult ?
|
||||
RCTCacheKeyForImage(request.URL.absoluteString, size, scale, resizeMode, fetchDate) : nil;
|
||||
|
||||
if (RCTIsMainQueue() && ![imageOrData isKindOfClass:[UIImage class]]) {
|
||||
// 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
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (!cancelled) {
|
||||
completionBlock(error, imageOrData, cacheResult);
|
||||
completionBlock(error, imageOrData, cacheKey);
|
||||
}
|
||||
});
|
||||
} else if (!cancelled) {
|
||||
completionBlock(error, imageOrData, cacheResult);
|
||||
completionBlock(error, imageOrData, cacheKey);
|
||||
}
|
||||
};
|
||||
|
||||
@ -336,28 +337,31 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
progressHandler:progressHandler
|
||||
completionHandler:completionHandler];
|
||||
completionHandler:^(NSError *error, UIImage *image){
|
||||
completionHandler(error, image, nil);
|
||||
}];
|
||||
}
|
||||
|
||||
// All access to URL cache must be serialized
|
||||
if (!_URLCacheQueue) {
|
||||
if (!_URLRequestQueue) {
|
||||
[self setUp];
|
||||
}
|
||||
|
||||
dispatch_async(_URLCacheQueue, ^{
|
||||
dispatch_async(_URLRequestQueue, ^{
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
if (cancelled || !strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find suitable image URL loader
|
||||
if (loadHandler) {
|
||||
cancelLoad = [loadHandler loadImageForURL:request.URL
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
progressHandler:progressHandler
|
||||
completionHandler:completionHandler];
|
||||
completionHandler:^(NSError *error, UIImage *image){
|
||||
completionHandler(error, image, nil);
|
||||
}];
|
||||
} else {
|
||||
// Use networking module to load image
|
||||
cancelLoad = [strongSelf _loadURLRequest:request
|
||||
@ -376,7 +380,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)_loadURLRequest:(NSURLRequest *)request
|
||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData))completionHandler
|
||||
completionBlock:(void (^)(NSError *error, id imageOrData, NSString *fetchDate))completionHandler
|
||||
{
|
||||
// Check if networking module is available
|
||||
if (RCT_DEBUG && ![_bridge respondsToSelector:@selector(networking)]) {
|
||||
@ -398,64 +402,42 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
RCTURLRequestCompletionBlock processResponse = ^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
// Check for system errors
|
||||
if (error) {
|
||||
completionHandler(error, nil);
|
||||
completionHandler(error, nil, nil);
|
||||
return;
|
||||
} else if (!response) {
|
||||
completionHandler(RCTErrorWithMessage(@"Response metadata error"), nil);
|
||||
completionHandler(RCTErrorWithMessage(@"Response metadata error"), nil, nil);
|
||||
return;
|
||||
} else if (!data) {
|
||||
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil);
|
||||
completionHandler(RCTErrorWithMessage(@"Unknown image download error"), nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for http errors
|
||||
NSString *responseDate;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
|
||||
if (statusCode != 200) {
|
||||
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
|
||||
code:statusCode
|
||||
userInfo:nil], nil);
|
||||
userInfo:nil], nil, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
responseDate = ((NSHTTPURLResponse *)response).allHeaderFields[@"Date"];
|
||||
}
|
||||
|
||||
// Call handler
|
||||
completionHandler(nil, data);
|
||||
completionHandler(nil, data, responseDate);
|
||||
};
|
||||
|
||||
// Check for cached response before reloading
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
if (!_URLCache) {
|
||||
_URLCache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB
|
||||
diskCapacity:200 * 1024 * 1024 // 200MB
|
||||
diskPath:@"React/RCTImageDownloader"];
|
||||
}
|
||||
|
||||
NSCachedURLResponse *cachedResponse = [_URLCache cachedResponseForRequest:request];
|
||||
while (cachedResponse) {
|
||||
if ([cachedResponse.response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)cachedResponse.response;
|
||||
if (httpResponse.statusCode == 301 || httpResponse.statusCode == 302) {
|
||||
NSString *location = httpResponse.allHeaderFields[@"Location"];
|
||||
if (location == nil) {
|
||||
completionHandler(RCTErrorWithMessage(@"Image redirect without location"), nil);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSURL *redirectURL = [NSURL URLWithString: location relativeToURL: request.URL];
|
||||
request = [NSURLRequest requestWithURL:redirectURL];
|
||||
cachedResponse = [_URLCache cachedResponseForRequest:request];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
processResponse(cachedResponse.response, cachedResponse.data, nil);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Download image
|
||||
__weak __typeof(self) weakSelf = self;
|
||||
RCTNetworkTask *task = [networking networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (error || !response || !data) {
|
||||
NSError *someError = nil;
|
||||
if (error) {
|
||||
@ -465,26 +447,12 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
} else {
|
||||
someError = RCTErrorWithMessage(@"Unknown image download error");
|
||||
}
|
||||
completionHandler(someError, nil);
|
||||
[weakSelf dequeueTasks];
|
||||
completionHandler(someError, nil, nil);
|
||||
[strongSelf dequeueTasks];
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch_async(self->_URLCacheQueue, ^{
|
||||
__typeof(self) strongSelf = self;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cache the response
|
||||
// TODO: move URL cache out of RCTImageLoader into its own module
|
||||
BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"];
|
||||
[strongSelf->_URLCache storeCachedResponse:
|
||||
[[NSCachedURLResponse alloc] initWithResponse:response
|
||||
data:data
|
||||
userInfo:nil
|
||||
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
|
||||
forRequest:request];
|
||||
dispatch_async(strongSelf->_URLRequestQueue, ^{
|
||||
// Process image data
|
||||
processResponse(response, data, nil);
|
||||
|
||||
@ -522,52 +490,52 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
__block void(^cancelLoad)(void) = nil;
|
||||
__weak RCTImageLoader *weakSelf = self;
|
||||
|
||||
// Check decoded image cache
|
||||
NSString *cacheKey = RCTCacheKeyForImage(imageURLRequest.URL.absoluteString, size, scale, resizeMode);
|
||||
{
|
||||
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
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
void (^completionHandler)(NSError *, id, NSString *) = ^(NSError *error, id imageOrData, NSString *cacheKey) {
|
||||
__typeof(self) strongSelf = weakSelf;
|
||||
if (cancelled || !strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
|
||||
completionBlock(error, imageOrData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check decoded image cache
|
||||
if (cacheKey) {
|
||||
UIImage *image = [strongSelf->_decodedImageCache objectForKey:cacheKey];
|
||||
if (image) {
|
||||
completionBlock(nil, image);
|
||||
});
|
||||
return ^{};
|
||||
}
|
||||
}
|
||||
|
||||
RCTImageLoaderCompletionBlock cacheResultHandler = ^(NSError *error, UIImage *image) {
|
||||
if (image) {
|
||||
CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * 4;
|
||||
if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) {
|
||||
[self->_decodedImageCache setObject:image forKey:cacheKey cost:bytes];
|
||||
return;
|
||||
}
|
||||
}
|
||||
completionBlock(error, image);
|
||||
};
|
||||
|
||||
void (^completionHandler)(NSError *, id, BOOL) = ^(NSError *error, id imageOrData, BOOL cacheResult) {
|
||||
if (!cancelled) {
|
||||
RCTImageLoaderCompletionBlock resultHandler = cacheResult ? cacheResultHandler : completionBlock;
|
||||
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
|
||||
resultHandler(error, imageOrData);
|
||||
} else {
|
||||
cancelLoad = [weakSelf decodeImageData:imageOrData
|
||||
size:size
|
||||
scale:scale
|
||||
clipped:clipped
|
||||
resizeMode:resizeMode
|
||||
completionBlock:resultHandler];
|
||||
// Store decoded image in cache
|
||||
RCTImageLoaderCompletionBlock cacheResultHandler = ^(NSError *error_, UIImage *image) {
|
||||
if (image) {
|
||||
CGFloat bytes = image.size.width * image.size.height * image.scale * image.scale * 4;
|
||||
if (bytes <= RCTMaxCachableDecodedImageSizeInBytes) {
|
||||
[self->_decodedImageCache setObject:image forKey:cacheKey cost:bytes];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completionBlock(error_, image);
|
||||
};
|
||||
|
||||
cancelLoad = [weakSelf decodeImageData:imageOrData
|
||||
size:size
|
||||
scale:scale
|
||||
clipped:clipped
|
||||
resizeMode:resizeMode
|
||||
completionBlock:cacheKey ? cacheResultHandler: completionBlock];
|
||||
};
|
||||
|
||||
cancelLoad = [self loadImageOrDataWithURLRequest:imageURLRequest
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
progressBlock:progressHandler
|
||||
completionBlock:completionHandler];
|
||||
cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest
|
||||
size:size
|
||||
scale:scale
|
||||
resizeMode:resizeMode
|
||||
progressBlock:progressHandler
|
||||
completionBlock:completionHandler];
|
||||
return ^{
|
||||
if (cancelLoad) {
|
||||
cancelLoad();
|
||||
@ -611,58 +579,54 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
resizeMode:resizeMode
|
||||
completionHandler:completionHandler] ?: ^{};
|
||||
} else {
|
||||
dispatch_block_t decodeBlock = ^{
|
||||
// Calculate the size, in bytes, that the decompressed image will require
|
||||
NSInteger decodedImageBytes = (size.width * scale) * (size.height * scale) * 4;
|
||||
|
||||
if (!_URLCacheQueue) {
|
||||
[self setUp];
|
||||
}
|
||||
dispatch_async(_URLCacheQueue, ^{
|
||||
dispatch_block_t decodeBlock = ^{
|
||||
// Mark these bytes as in-use
|
||||
self->_activeBytes += decodedImageBytes;
|
||||
|
||||
// Calculate the size, in bytes, that the decompressed image will require
|
||||
NSInteger decodedImageBytes = (size.width * scale) * (size.height * scale) * 4;
|
||||
// Do actual decompression on a concurrent background queue
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (!cancelled) {
|
||||
|
||||
// Mark these bytes as in-use
|
||||
self->_activeBytes += decodedImageBytes;
|
||||
|
||||
// Do actual decompression on a concurrent background queue
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
if (!cancelled) {
|
||||
|
||||
// Decompress the image data (this may be CPU and memory intensive)
|
||||
UIImage *image = RCTDecodeImageWithData(data, size, scale, resizeMode);
|
||||
// Decompress the image data (this may be CPU and memory intensive)
|
||||
UIImage *image = RCTDecodeImageWithData(data, size, scale, resizeMode);
|
||||
|
||||
#if RCT_DEV
|
||||
|
||||
CGSize imagePixelSize = RCTSizeInPixels(image.size, image.scale);
|
||||
CGSize screenPixelSize = RCTSizeInPixels(RCTScreenSize(), RCTScreenScale());
|
||||
if (imagePixelSize.width * imagePixelSize.height >
|
||||
screenPixelSize.width * screenPixelSize.height) {
|
||||
RCTLogInfo(@"[PERF ASSETS] Loading image at size %@, which is larger "
|
||||
"than the screen size %@", NSStringFromCGSize(imagePixelSize),
|
||||
NSStringFromCGSize(screenPixelSize));
|
||||
}
|
||||
|
||||
CGSize imagePixelSize = RCTSizeInPixels(image.size, image.scale);
|
||||
CGSize screenPixelSize = RCTSizeInPixels(RCTScreenSize(), RCTScreenScale());
|
||||
if (imagePixelSize.width * imagePixelSize.height >
|
||||
screenPixelSize.width * screenPixelSize.height) {
|
||||
RCTLogInfo(@"[PERF ASSETS] Loading image at size %@, which is larger "
|
||||
"than the screen size %@", NSStringFromCGSize(imagePixelSize),
|
||||
NSStringFromCGSize(screenPixelSize));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (image) {
|
||||
completionHandler(nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
|
||||
NSError *finalError = RCTErrorWithMessage(errorMessage);
|
||||
completionHandler(finalError, nil);
|
||||
}
|
||||
if (image) {
|
||||
completionHandler(nil, image);
|
||||
} else {
|
||||
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
|
||||
NSError *finalError = RCTErrorWithMessage(errorMessage);
|
||||
completionHandler(finalError, nil);
|
||||
}
|
||||
}
|
||||
|
||||
// We're no longer retaining the uncompressed data, so now we'll mark
|
||||
// the decoding as complete so that the loading task queue can resume.
|
||||
dispatch_async(self->_URLCacheQueue, ^{
|
||||
self->_scheduledDecodes--;
|
||||
self->_activeBytes -= decodedImageBytes;
|
||||
[self dequeueTasks];
|
||||
});
|
||||
// We're no longer retaining the uncompressed data, so now we'll mark
|
||||
// the decoding as complete so that the loading task queue can resume.
|
||||
dispatch_async(self->_URLRequestQueue, ^{
|
||||
self->_scheduledDecodes--;
|
||||
self->_activeBytes -= decodedImageBytes;
|
||||
[self dequeueTasks];
|
||||
});
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
if (!_URLRequestQueue) {
|
||||
[self setUp];
|
||||
}
|
||||
dispatch_async(_URLRequestQueue, ^{
|
||||
// The decode operation retains the compressed image data until it's
|
||||
// complete, so we'll mark it as having started, in order to block
|
||||
// further image loads from happening until we're done with the data.
|
||||
@ -678,7 +642,6 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
} else {
|
||||
[self->_pendingDecodes addObject:decodeBlock];
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return ^{
|
||||
@ -688,30 +651,32 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||
}
|
||||
|
||||
- (RCTImageLoaderCancellationBlock)getImageSizeForURLRequest:(NSURLRequest *)imageURLRequest
|
||||
block:(void(^)(NSError *error, CGSize size))completionBlock
|
||||
block:(void(^)(NSError *error, CGSize size))callback
|
||||
{
|
||||
return [self loadImageOrDataWithURLRequest:imageURLRequest
|
||||
size:CGSizeZero
|
||||
scale:1
|
||||
resizeMode:RCTResizeModeStretch
|
||||
progressBlock:nil
|
||||
completionBlock:^(NSError *error, id imageOrData, __unused BOOL cacheResult) {
|
||||
CGSize size;
|
||||
if ([imageOrData isKindOfClass:[NSData class]]) {
|
||||
NSDictionary *meta = RCTGetImageMetadata(imageOrData);
|
||||
size = (CGSize){
|
||||
[meta[(id)kCGImagePropertyPixelWidth] doubleValue],
|
||||
[meta[(id)kCGImagePropertyPixelHeight] doubleValue],
|
||||
};
|
||||
} else {
|
||||
UIImage *image = imageOrData;
|
||||
size = (CGSize){
|
||||
image.size.width * image.scale,
|
||||
image.size.height * image.scale,
|
||||
};
|
||||
}
|
||||
completionBlock(error, size);
|
||||
}];
|
||||
void (^completion)(NSError *, id, NSString *) = ^(NSError *error, id imageOrData, NSString *cacheKey) {
|
||||
CGSize size;
|
||||
if ([imageOrData isKindOfClass:[NSData class]]) {
|
||||
NSDictionary *meta = RCTGetImageMetadata(imageOrData);
|
||||
size = (CGSize){
|
||||
[meta[(id)kCGImagePropertyPixelWidth] doubleValue],
|
||||
[meta[(id)kCGImagePropertyPixelHeight] doubleValue],
|
||||
};
|
||||
} else {
|
||||
UIImage *image = imageOrData;
|
||||
size = (CGSize){
|
||||
image.size.width * image.scale,
|
||||
image.size.height * image.scale,
|
||||
};
|
||||
}
|
||||
callback(error, size);
|
||||
};
|
||||
|
||||
return [self _loadImageOrDataWithURLRequest:imageURLRequest
|
||||
size:CGSizeZero
|
||||
scale:1
|
||||
resizeMode:RCTResizeModeStretch
|
||||
progressBlock:NULL
|
||||
completionBlock:completion];
|
||||
}
|
||||
|
||||
#pragma mark - RCTURLRequestHandler
|
||||
|
Loading…
x
Reference in New Issue
Block a user