Add some defensive cleanup of cancelBlock in RCTImageLoader
Reviewed By: mmmulani Differential Revision: D3742177 fbshipit-source-id: 1cd16c2519052ec5811ecadf2530a5846b4ae1bc
This commit is contained in:
Родитель
0082517a6c
Коммит
14188289fc
|
@ -294,10 +294,6 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
||||||
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate))completionBlock
|
completionBlock:(void (^)(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate))completionBlock
|
||||||
{
|
{
|
||||||
__block volatile uint32_t cancelled = 0;
|
|
||||||
__block dispatch_block_t cancelLoad = nil;
|
|
||||||
__weak RCTImageLoader *weakSelf = self;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
NSMutableURLRequest *mutableRequest = [request mutableCopy];
|
||||||
[NSURLProtocol setProperty:@"RCTImageLoader"
|
[NSURLProtocol setProperty:@"RCTImageLoader"
|
||||||
|
@ -316,9 +312,14 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
BOOL requiresScheduling = [loadHandler respondsToSelector:@selector(requiresScheduling)] ?
|
BOOL requiresScheduling = [loadHandler respondsToSelector:@selector(requiresScheduling)] ?
|
||||||
[loadHandler requiresScheduling] : YES;
|
[loadHandler requiresScheduling] : YES;
|
||||||
|
|
||||||
|
__block volatile uint32_t cancelled = 0;
|
||||||
|
__block dispatch_block_t cancelLoad = nil;
|
||||||
void (^completionHandler)(NSError *, id, NSString *) = ^(NSError *error, id imageOrData, NSString *fetchDate) {
|
void (^completionHandler)(NSError *, id, NSString *) = ^(NSError *error, id imageOrData, NSString *fetchDate) {
|
||||||
|
cancelLoad = nil;
|
||||||
|
|
||||||
BOOL cacheResult = [loadHandler respondsToSelector:@selector(shouldCacheLoadedImages)] ?
|
BOOL cacheResult = [loadHandler respondsToSelector:@selector(shouldCacheLoadedImages)] ?
|
||||||
[loadHandler shouldCacheLoadedImages] : YES;
|
[loadHandler shouldCacheLoadedImages] : YES;
|
||||||
|
|
||||||
// If we've received an image, we should try to set it synchronously,
|
// If we've received an image, we should try to set it synchronously,
|
||||||
// if it's data, do decoding on a background thread.
|
// if it's data, do decoding on a background thread.
|
||||||
if (RCTIsMainQueue() && ![imageOrData isKindOfClass:[UIImage class]]) {
|
if (RCTIsMainQueue() && ![imageOrData isKindOfClass:[UIImage class]]) {
|
||||||
|
@ -352,6 +353,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
[self setUp];
|
[self setUp];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__weak RCTImageLoader *weakSelf = self;
|
||||||
dispatch_async(_URLRequestQueue, ^{
|
dispatch_async(_URLRequestQueue, ^{
|
||||||
__typeof(self) strongSelf = weakSelf;
|
__typeof(self) strongSelf = weakSelf;
|
||||||
if (cancelled || !strongSelf) {
|
if (cancelled || !strongSelf) {
|
||||||
|
@ -378,6 +380,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
return ^{
|
return ^{
|
||||||
if (cancelLoad) {
|
if (cancelLoad) {
|
||||||
cancelLoad();
|
cancelLoad();
|
||||||
|
cancelLoad = nil;
|
||||||
}
|
}
|
||||||
OSAtomicOr32Barrier(1, &cancelled);
|
OSAtomicOr32Barrier(1, &cancelled);
|
||||||
};
|
};
|
||||||
|
@ -464,7 +467,6 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
// Prepare for next task
|
// Prepare for next task
|
||||||
[strongSelf dequeueTasks];
|
[strongSelf dequeueTasks];
|
||||||
});
|
});
|
||||||
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
task.downloadProgressBlock = progressHandler;
|
task.downloadProgressBlock = progressHandler;
|
||||||
|
@ -488,13 +490,19 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
scale:(CGFloat)scale
|
scale:(CGFloat)scale
|
||||||
clipped:(BOOL)clipped
|
clipped:(BOOL)clipped
|
||||||
resizeMode:(RCTResizeMode)resizeMode
|
resizeMode:(RCTResizeMode)resizeMode
|
||||||
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
|
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
|
||||||
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
|
||||||
{
|
{
|
||||||
__block volatile uint32_t cancelled = 0;
|
__block volatile uint32_t cancelled = 0;
|
||||||
__block void(^cancelLoad)(void) = nil;
|
__block dispatch_block_t cancelLoad = nil;
|
||||||
__weak RCTImageLoader *weakSelf = self;
|
dispatch_block_t cancellationBlock = ^{
|
||||||
|
if (cancelLoad) {
|
||||||
|
cancelLoad();
|
||||||
|
}
|
||||||
|
OSAtomicOr32Barrier(1, &cancelled);
|
||||||
|
};
|
||||||
|
|
||||||
|
__weak RCTImageLoader *weakSelf = self;
|
||||||
void (^completionHandler)(NSError *, id, BOOL, NSString *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate) {
|
void (^completionHandler)(NSError *, id, BOOL, NSString *) = ^(NSError *error, id imageOrData, BOOL cacheResult, NSString *fetchDate) {
|
||||||
__typeof(self) strongSelf = weakSelf;
|
__typeof(self) strongSelf = weakSelf;
|
||||||
if (cancelled || !strongSelf) {
|
if (cancelled || !strongSelf) {
|
||||||
|
@ -502,6 +510,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
|
if (!imageOrData || [imageOrData isKindOfClass:[UIImage class]]) {
|
||||||
|
cancelLoad = nil;
|
||||||
completionBlock(error, imageOrData);
|
completionBlock(error, imageOrData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -514,14 +523,15 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
resizeMode:resizeMode
|
resizeMode:resizeMode
|
||||||
responseDate:fetchDate];
|
responseDate:fetchDate];
|
||||||
if (image) {
|
if (image) {
|
||||||
|
cancelLoad = nil;
|
||||||
completionBlock(nil, image);
|
completionBlock(nil, image);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCTImageLoaderCompletionBlock decodeCompletionHandler = ^(NSError *error_, UIImage *image) {
|
||||||
|
if (cacheResult && image) {
|
||||||
// Store decoded image in cache
|
// Store decoded image in cache
|
||||||
RCTImageLoaderCompletionBlock cacheResultHandler = ^(NSError *error_, UIImage *image) {
|
|
||||||
if (image) {
|
|
||||||
[[strongSelf imageCache] addImageToCache:image
|
[[strongSelf imageCache] addImageToCache:image
|
||||||
URL:imageURLRequest.URL.absoluteString
|
URL:imageURLRequest.URL.absoluteString
|
||||||
size:size
|
size:size
|
||||||
|
@ -530,6 +540,7 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
responseDate:fetchDate];
|
responseDate:fetchDate];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cancelLoad = nil;
|
||||||
completionBlock(error_, image);
|
completionBlock(error_, image);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -538,21 +549,16 @@ static UIImage *RCTResizeImageIfNeeded(UIImage *image,
|
||||||
scale:scale
|
scale:scale
|
||||||
clipped:clipped
|
clipped:clipped
|
||||||
resizeMode:resizeMode
|
resizeMode:resizeMode
|
||||||
completionBlock:cacheResult ? cacheResultHandler: completionBlock];
|
completionBlock:decodeCompletionHandler];
|
||||||
};
|
};
|
||||||
|
|
||||||
cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest
|
cancelLoad = [self _loadImageOrDataWithURLRequest:imageURLRequest
|
||||||
size:size
|
size:size
|
||||||
scale:scale
|
scale:scale
|
||||||
resizeMode:resizeMode
|
resizeMode:resizeMode
|
||||||
progressBlock:progressHandler
|
progressBlock:progressBlock
|
||||||
completionBlock:completionHandler];
|
completionBlock:completionHandler];
|
||||||
return ^{
|
return cancellationBlock;
|
||||||
if (cancelLoad) {
|
|
||||||
cancelLoad();
|
|
||||||
}
|
|
||||||
OSAtomicOr32Barrier(1, &cancelled);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
|
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
|
||||||
|
|
|
@ -273,8 +273,11 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
}
|
}
|
||||||
|
|
||||||
RCTImageSource *source = _imageSource;
|
RCTImageSource *source = _imageSource;
|
||||||
CGFloat blurRadius = _blurRadius;
|
|
||||||
__weak RCTImageView *weakSelf = self;
|
__weak RCTImageView *weakSelf = self;
|
||||||
|
RCTImageLoaderCompletionBlock completionHandler = ^(NSError *error, UIImage *loadedImage) {
|
||||||
|
[weakSelf imageLoaderLoadedImage:loadedImage error:error forImageSource:source];
|
||||||
|
};
|
||||||
|
|
||||||
_reloadImageCancellationBlock =
|
_reloadImageCancellationBlock =
|
||||||
[_bridge.imageLoader loadImageWithURLRequest:source.request
|
[_bridge.imageLoader loadImageWithURLRequest:source.request
|
||||||
size:imageSize
|
size:imageSize
|
||||||
|
@ -282,40 +285,51 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
clipped:NO
|
clipped:NO
|
||||||
resizeMode:_resizeMode
|
resizeMode:_resizeMode
|
||||||
progressBlock:progressHandler
|
progressBlock:progressHandler
|
||||||
completionBlock:^(NSError *error, UIImage *loadedImage) {
|
completionBlock:completionHandler];
|
||||||
|
} else {
|
||||||
|
[self clearImage];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
RCTImageView *strongSelf = weakSelf;
|
- (void)imageLoaderLoadedImage:(UIImage *)loadedImage error:(NSError *)error forImageSource:(RCTImageSource *)source
|
||||||
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
|
{
|
||||||
if (![source isEqual:strongSelf.imageSource]) {
|
if (![source isEqual:_imageSource]) {
|
||||||
// Bail out if source has changed since we started loading
|
// Bail out if source has changed since we started loading
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (image.reactKeyframeAnimation) {
|
|
||||||
[strongSelf.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
|
|
||||||
} else {
|
|
||||||
[strongSelf.layer removeAnimationForKey:@"contents"];
|
|
||||||
strongSelf.image = image;
|
|
||||||
}
|
|
||||||
if (error) {
|
if (error) {
|
||||||
if (strongSelf->_onError) {
|
if (_onError) {
|
||||||
strongSelf->_onError(@{ @"error": error.localizedDescription });
|
_onError(@{ @"error": error.localizedDescription });
|
||||||
}
|
}
|
||||||
|
if (_onLoadEnd) {
|
||||||
|
_onLoadEnd(nil);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void (^setImageBlock)(UIImage *) = ^(UIImage *image) {
|
||||||
|
if (image.reactKeyframeAnimation) {
|
||||||
|
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
|
||||||
} else {
|
} else {
|
||||||
if (strongSelf->_onLoad) {
|
[self.layer removeAnimationForKey:@"contents"];
|
||||||
strongSelf->_onLoad(nil);
|
self.image = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (self->_onLoad) {
|
||||||
|
self->_onLoad(nil);
|
||||||
}
|
}
|
||||||
if (strongSelf->_onLoadEnd) {
|
if (self->_onLoadEnd) {
|
||||||
strongSelf->_onLoadEnd(nil);
|
self->_onLoadEnd(nil);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (blurRadius > __FLT_EPSILON__) {
|
if (_blurRadius > __FLT_EPSILON__) {
|
||||||
// Blur on a background thread to avoid blocking interaction
|
// Blur on a background thread to avoid blocking interaction
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
UIImage *image = RCTBlurredImageWithRadius(loadedImage, blurRadius);
|
UIImage *blurredImage = RCTBlurredImageWithRadius(loadedImage, self->_blurRadius);
|
||||||
RCTExecuteOnMainQueue(^{
|
RCTExecuteOnMainQueue(^{
|
||||||
setImageBlock(image);
|
setImageBlock(blurredImage);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -327,10 +341,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
setImageBlock(loadedImage);
|
setImageBlock(loadedImage);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}];
|
|
||||||
} else {
|
|
||||||
[self clearImage];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)reactSetFrame:(CGRect)frame
|
- (void)reactSetFrame:(CGRect)frame
|
||||||
|
|
|
@ -78,9 +78,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
- (void)cancel
|
- (void)cancel
|
||||||
{
|
{
|
||||||
_status = RCTNetworkTaskFinished;
|
_status = RCTNetworkTaskFinished;
|
||||||
__strong id strongToken = _requestToken;
|
id token = _requestToken;
|
||||||
if (strongToken && [_handler respondsToSelector:@selector(cancelRequest:)]) {
|
if (token && [_handler respondsToSelector:@selector(cancelRequest:)]) {
|
||||||
[_handler cancelRequest:strongToken];
|
[_handler cancelRequest:token];
|
||||||
}
|
}
|
||||||
[self invalidate];
|
[self invalidate];
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче