Merge branch 'master' of https://github.com/rs/SDWebImage into 5.x

* 'master' of https://github.com/rs/SDWebImage:
  Add protect to some Core Graphics methods
  Change all UIImage init method to alloc instead of autorelease to immediately release it after usage
  Add a option SDWebImageFromCacheOnly to load the image from cache only and prevent network
  Update the test for custom operation interface
  Use synchronized instead of semaphore in SDWebImageDownloader to make it more easy to understand :)
  Use a lock instead of barrier queue to avoid dispatch_sync blocking the main queue on race condition
This commit is contained in:
DreamPiggy 2018-01-23 11:51:49 +08:00
commit edd26fc1e6
7 changed files with 86 additions and 60 deletions

View File

@ -36,8 +36,6 @@
@property (assign, nonatomic, nullable) Class operationClass;
@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;
@property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders;
// This queue is used to serialize the handling of the network responses of all the download operation in a single queue
@property (strong, nonatomic, nullable) dispatch_queue_t barrierQueue;
// The session in which data tasks will run
@property (strong, nonatomic) NSURLSession *session;
@ -96,7 +94,6 @@
#else
_HTTPHeaders = [@{@"Accept": @"image/*;q=0.8"} mutableCopy];
#endif
_barrierQueue = dispatch_queue_create("com.hackemist.SDWebImageDownloaderBarrierQueue", DISPATCH_QUEUE_CONCURRENT);
_downloadTimeout = 15.0;
[self createNewSessionWithConfiguration:sessionConfiguration];
@ -231,13 +228,15 @@
}
- (void)cancel:(nullable SDWebImageDownloadToken *)token {
dispatch_barrier_async(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[token.url];
BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
if (canceled) {
[self.URLOperations removeObjectForKey:token.url];
}
});
NSURL *url = token.url;
if (!url) {
return;
}
SDWebImageDownloaderOperation *operation = [self operationForURL:url];
BOOL canceled = [operation cancel:token.downloadOperationCancelToken];
if (canceled) {
[self removeOperationForURL:url];
}
}
- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
@ -251,33 +250,24 @@
}
return nil;
}
__block SDWebImageDownloadToken *token = nil;
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *operation = self.URLOperations[url];
if (!operation) {
operation = createCallback();
self.URLOperations[url] = operation;
__weak SDWebImageDownloaderOperation *woperation = operation;
operation.completionBlock = ^{
dispatch_barrier_sync(self.barrierQueue, ^{
SDWebImageDownloaderOperation *soperation = woperation;
if (!soperation) return;
if (self.URLOperations[url] == soperation) {
[self.URLOperations removeObjectForKey:url];
};
});
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
token = [SDWebImageDownloadToken new];
token.downloadOperation = operation;
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
});
SDWebImageDownloaderOperation *operation = [self operationForURL:url];
if (!operation) {
operation = createCallback();
[self setOperation:operation forURL:url];
__weak typeof(self) wself = self;
operation.completionBlock = ^{
__strong typeof(wself) sself = wself;
[sself removeOperationForURL:url];
};
}
id downloadOperationCancelToken = [operation addHandlersForProgress:progressBlock completed:completedBlock];
SDWebImageDownloadToken *token = [SDWebImageDownloadToken new];
token.downloadOperation = operation;
token.url = url;
token.downloadOperationCancelToken = downloadOperationCancelToken;
return token;
}
@ -292,6 +282,35 @@
#pragma mark Helper methods
- (SDWebImageDownloaderOperation *)operationForURL:(NSURL *)url {
if (!url) {
return nil;
}
SDWebImageDownloaderOperation *operation;
@synchronized (self.URLOperations) {
operation = [self.URLOperations objectForKey:url];
}
return operation;
}
- (void)setOperation:(SDWebImageDownloaderOperation *)operation forURL:(NSURL *)url {
if (!operation || !url) {
return;
}
@synchronized (self.URLOperations) {
[self.URLOperations setObject:operation forKey:url];
}
}
- (void)removeOperationForURL:(NSURL *)url {
if (!url) {
return;
}
@synchronized (self.URLOperations) {
[self.URLOperations removeObjectForKey:url];
}
}
- (SDWebImageDownloaderOperation *)operationWithTask:(NSURLSessionTask *)task {
SDWebImageDownloaderOperation *returnOperation = nil;
for (SDWebImageDownloaderOperation *operation in self.downloadQueue.operations) {

View File

@ -58,14 +58,7 @@
}
float duration = [self sd_frameDurationAtIndex:i source:source];
#if SD_WATCH
CGFloat scale = 1;
scale = [WKInterfaceDevice currentDevice].screenScale;
#elif SD_UIKIT
CGFloat scale = 1;
scale = [UIScreen mainScreen].scale;
#endif
UIImage *image = [UIImage imageWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef];
CGImageRelease(imageRef);
SDWebImageFrame *frame = [SDWebImageFrame frameWithImage:image duration:duration];
@ -95,6 +88,9 @@
- (float)sd_frameDurationAtIndex:(NSUInteger)index source:(CGImageSourceRef)source {
float frameDuration = 0.1f;
CFDictionaryRef cfFrameProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
if (!cfFrameProperties) {
return frameDuration;
}
NSDictionary *frameProperties = (__bridge NSDictionary *)cfFrameProperties;
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary];

View File

@ -107,9 +107,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
UIImageOrientation orientation = [[self class] sd_imageOrientationFromImageData:data];
if (orientation != UIImageOrientationUp) {
image = [UIImage imageWithCGImage:image.CGImage
scale:image.scale
orientation:orientation];
image = [[UIImage alloc] initWithCGImage:image.CGImage scale:image.scale orientation:orientation];
}
return image;
@ -175,7 +173,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
if (partialImageRef) {
#if SD_UIKIT || SD_WATCH
image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:_orientation];
image = [[UIImage alloc] initWithCGImage:partialImageRef scale:1 orientation:_orientation];
#elif SD_MAC
image = [[UIImage alloc] initWithCGImage:partialImageRef size:NSZeroSize];
#endif
@ -260,10 +258,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
// Draw the image into the context and retrieve the new bitmap image without alpha
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
CGImageRef imageRefWithoutAlpha = CGBitmapContextCreateImage(context);
UIImage *imageWithoutAlpha = [UIImage imageWithCGImage:imageRefWithoutAlpha
scale:image.scale
orientation:image.imageOrientation];
UIImage *imageWithoutAlpha = [[UIImage alloc] initWithCGImage:imageRefWithoutAlpha scale:image.scale orientation:image.imageOrientation];
CGContextRelease(context);
CGImageRelease(imageRefWithoutAlpha);
@ -378,7 +373,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
if (destImageRef == NULL) {
return image;
}
UIImage *destImage = [UIImage imageWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation];
UIImage *destImage = [[UIImage alloc] initWithCGImage:destImageRef scale:image.scale orientation:image.imageOrientation];
CGImageRelease(destImageRef);
if (destImage == nil) {
return image;

View File

@ -98,15 +98,20 @@ typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
/**
* By default, we do not query disk data when the image is cached in memory. This mask can force to query disk data at the same time.
* This options is recommend to be used with `SDWebImageQueryDiskSync` to ensure the image is loaded in the same runloop.
* This flag is recommend to be used with `SDWebImageQueryDiskSync` to ensure the image is loaded in the same runloop.
*/
SDWebImageQueryDataWhenInMemory = 1 << 13,
/**
* By default, we query the memory cache synchronously, disk cache asynchronously. This mask can force to query disk cache synchronously to ensure that image is loaded in the same runloop.
* This can avoid flashing during cell reuse if you disable memory cache or in some other cases.
* This flag can avoid flashing during cell reuse if you disable memory cache or in some other cases.
*/
SDWebImageQueryDiskSync = 1 << 14
SDWebImageQueryDiskSync = 1 << 14,
/**
* By default, when the cache missed, the image is download from the network. This flag can prevent network to load from cache only.
*/
SDWebImageFromCacheOnly = 1 << 15
};
typedef void(^SDExternalCompletionBlock)(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL);

View File

@ -157,8 +157,12 @@
[self safelyRemoveOperationFromRunning:strongOperation];
return;
}
if ((!cachedImage || options & SDWebImageRefreshCached) && (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url])) {
// Check whether we should download image from network
BOOL shouldDownload = (!(options & SDWebImageFromCacheOnly))
&& (!cachedImage || options & SDWebImageRefreshCached)
&& (![self.delegate respondsToSelector:@selector(imageManager:shouldDownloadImageForURL:)] || [self.delegate imageManager:self shouldDownloadImageForURL:url]);
if (shouldDownload) {
if (cachedImage && options & SDWebImageRefreshCached) {
// If image was found in the cache but SDWebImageRefreshCached is provided, notify about the cached image
// AND try to re-download it in order to let a chance to NSURLCache to refresh it from server.

View File

@ -254,7 +254,7 @@
CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
#if SD_UIKIT || SD_WATCH
image = [UIImage imageWithCGImage:newImageRef];
image = [[UIImage alloc] initWithCGImage:newImageRef];
#elif SD_MAC
image = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize];
#endif
@ -394,6 +394,9 @@
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
if (!dataProvider) {
return nil;
}
CFDataRef dataRef = CGDataProviderCopyData(dataProvider);
uint8_t *rgba = (uint8_t *)CFDataGetBytePtr(dataRef);

View File

@ -48,6 +48,10 @@
return nil;
}
- (BOOL)cancel:(id)token {
return YES;
}
@end