Add some defensive cleanup of cancelBlock in RCTImageLoader

Reviewed By: mmmulani

Differential Revision: D3742177

fbshipit-source-id: 1cd16c2519052ec5811ecadf2530a5846b4ae1bc
This commit is contained in:
Pieter De Baets 2016-08-19 10:31:42 -07:00 коммит произвёл Facebook Github Bot 0
Родитель 0082517a6c
Коммит 14188289fc
4 изменённых файлов: 102 добавлений и 86 удалений

Просмотреть файл

@ -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];
} }