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:
commit
edd26fc1e6
|
@ -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) {
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -48,6 +48,10 @@
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)cancel:(id)token {
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue