每張圖片的下載都有一個(gè)Operation操作,操作的創(chuàng)建和加入已在方法
-downloadImageWithURL:options:progress:completed:
-addProgressCallback:completedBlock:forURL:createCallback:
中進(jìn)行。
操作的初始化方法為:
- (id)initWithRequest:(NSURLRequest *)request
inSession:(NSURLSession *)session
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock;
SDWebImageDownloaderOperation遵守 SDWebImageOperation中的協(xié)議:
- (void)cancel;
代碼注釋如下:
/*
* This file is part of the SDWebImage package.
* (c) Olivier Poitrey <rs@dailymotion.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#import "SDWebImageDownloaderOperation.h"
#import "SDWebImageDecoder.h"
#import "UIImage+MultiFormat.h"
#import <ImageIO/ImageIO.h>
#import "SDWebImageManager.h"
NSString *const SDWebImageDownloadStartNotification = @"SDWebImageDownloadStartNotification";
NSString *const SDWebImageDownloadReceiveResponseNotification = @"SDWebImageDownloadReceiveResponseNotification";
NSString *const SDWebImageDownloadStopNotification = @"SDWebImageDownloadStopNotification";
NSString *const SDWebImageDownloadFinishNotification = @"SDWebImageDownloadFinishNotification";
@interface SDWebImageDownloaderOperation ()
@property (copy, nonatomic) SDWebImageDownloaderProgressBlock progressBlock;
@property (copy, nonatomic) SDWebImageDownloaderCompletedBlock completedBlock;
@property (copy, nonatomic) SDWebImageNoParamsBlock cancelBlock;
// 是否正在執(zhí)行
@property (assign, nonatomic, getter = isExecuting) BOOL executing;
// 是否已完成
@property (assign, nonatomic, getter = isFinished) BOOL finished;
// 圖片數(shù)據(jù)
@property (strong, nonatomic) NSMutableData *imageData;
// This is weak because it is injected by whoever manages this session. If this gets nil-ed out, we won't be able to run
// the task associated with this operation
// URL連接會(huì)話(huà):因管理此會(huì)話(huà)的注入所以采用weak修飾。如不為nil,則無(wú)法運(yùn)行此操作關(guān)聯(lián)的任務(wù)
@property (weak, nonatomic) NSURLSession *unownedSession;
// This is set if we're using not using an injected NSURLSession. We're responsible of invalidating this one
// URL連接會(huì)話(huà):strong修飾,不太理解這兩個(gè)區(qū)別??????
@property (strong, nonatomic) NSURLSession *ownedSession;
// URL連接會(huì)話(huà)任務(wù):用于圖片下載數(shù)據(jù)傳輸。擴(kuò)展:http://www.cnblogs.com/biosli/p/iOS_Network_URL_Session.html
@property (strong, nonatomic, readwrite) NSURLSessionTask *dataTask;
@property (strong, atomic) NSThread *thread;
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
// app退到后臺(tái)時(shí),主線(xiàn)程會(huì)被暫停。但想在后臺(tái)完成一個(gè)長(zhǎng)期任務(wù),須調(diào)用UIApplication的beginBackgroundTaskWithExpirationHandler:實(shí)例方法,來(lái)向 iOS 借點(diǎn)時(shí)間
// UIBackgroundTaskIdentifier相當(dāng)于一個(gè)借據(jù),告訴iOS,程序?qū)⒁韪嗟臅r(shí)間來(lái)完成Long-Running Task任務(wù)
@property (assign, nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
#endif
@end
@implementation SDWebImageDownloaderOperation {
size_t width, height; // 圖片的寬、高
UIImageOrientation orientation; // 圖片方向
BOOL responseFromCached; // 是否從緩存中響應(yīng)
}
@synthesize executing = _executing;
@synthesize finished = _finished;
- (id)initWithRequest:(NSURLRequest *)request
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
return [self initWithRequest:request
inSession:nil
options:options
progress:progressBlock
completed:completedBlock
cancelled:cancelBlock];
}
- (id)initWithRequest:(NSURLRequest *)request
inSession:(NSURLSession *)session
options:(SDWebImageDownloaderOptions)options
progress:(SDWebImageDownloaderProgressBlock)progressBlock
completed:(SDWebImageDownloaderCompletedBlock)completedBlock
cancelled:(SDWebImageNoParamsBlock)cancelBlock {
if ((self = [super init])) {
_request = request;
_shouldDecompressImages = YES;
_options = options;
_progressBlock = [progressBlock copy];
_completedBlock = [completedBlock copy];
_cancelBlock = [cancelBlock copy];
_executing = NO;
_finished = NO;
_expectedSize = 0;
_unownedSession = session;
responseFromCached = YES; // Initially wrong until `- URLSession:dataTask:willCacheResponse:completionHandler: is called or not called
}
return self;
}
/**
* 復(fù)寫(xiě)NSOperation的start方法:沒(méi)有簡(jiǎn)單的實(shí)現(xiàn)main方法,而是采用更加靈活的start方法,以便自己管理下載的狀態(tài),創(chuàng)建了下載所使用的NSURLSession對(duì)象
*/
- (void)start {
// @synchronized 的作用是創(chuàng)建一個(gè)互斥鎖,保證此時(shí)沒(méi)有其它線(xiàn)程對(duì)self對(duì)象進(jìn)行修改。這個(gè)是objective-c的一個(gè)鎖定令牌,防止self對(duì)象在同一時(shí)間內(nèi)被其它線(xiàn)程訪(fǎng)問(wèn),起到線(xiàn)程的保護(hù)作用。
@synchronized (self) {
// 管理下載狀態(tài),如果已取消,則重置當(dāng)前下載并設(shè)置完成狀態(tài)YES
if (self.isCancelled) {
self.finished = YES;
[self reset];
return;
}
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
Class UIApplicationClass = NSClassFromString(@"UIApplication");
BOOL hasApplication = UIApplicationClass && [UIApplicationClass respondsToSelector:@selector(sharedApplication)];
// 是否允許后臺(tái)執(zhí)行
if (hasApplication && [self shouldContinueWhenAppEntersBackground]) {
__weak __typeof__ (self) wself = self;
UIApplication * app = [UIApplicationClass performSelector:@selector(sharedApplication)];
// 標(biāo)記一個(gè)可以在后臺(tái)長(zhǎng)時(shí)間運(yùn)行的后臺(tái)任務(wù)
self.backgroundTaskId = [app beginBackgroundTaskWithExpirationHandler:^{
__strong __typeof (wself) sself = wself;
// 當(dāng)應(yīng)用程序留給后臺(tái)的時(shí)間快要結(jié)束時(shí)(該時(shí)間有限),這個(gè)block將執(zhí)行:進(jìn)行一些清理工作(主線(xiàn)程執(zhí)行),清理失敗會(huì)導(dǎo)致程序掛掉
if (sself) {
[sself cancel];
// 標(biāo)記指定的后臺(tái)任務(wù)完成
[app endBackgroundTask:sself.backgroundTaskId];
// 銷(xiāo)毀后臺(tái)任務(wù)標(biāo)識(shí)符
sself.backgroundTaskId = UIBackgroundTaskInvalid;
}
}];
}
#endif
NSURLSession *session = self.unownedSession;
if (!self.unownedSession) {
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
sessionConfig.timeoutIntervalForRequest = 15;
/**
* Create the session for this task
* We send nil as delegate queue so that the session creates a serial operation queue for performing all delegate
* method calls and completion handler calls.
*/
// delegateQueue設(shè)置為nil(設(shè)置delegate在哪個(gè)OperationQueue回調(diào)):以便創(chuàng)建一個(gè)用于執(zhí)行所有委托方法和完成處理程序調(diào)用的串行操作隊(duì)列
self.ownedSession = [NSURLSession sessionWithConfiguration:sessionConfig
delegate:self
delegateQueue:nil];
session = self.ownedSession;
}
// 創(chuàng)建數(shù)據(jù)任務(wù)
self.dataTask = [session dataTaskWithRequest:self.request];
self.executing = YES;
self.thread = [NSThread currentThread];
}
// 啟動(dòng)任務(wù)
[self.dataTask resume];
if (self.dataTask) {
if (self.progressBlock) {
// 剛啟動(dòng),還不知道圖片大小
self.progressBlock(0, NSURLResponseUnknownLength);
}
// 在主線(xiàn)程發(fā)送下載開(kāi)始通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStartNotification object:self];
});
}
else {
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Connection can't be initialized"}], YES);
}
}
// 確保后臺(tái)任務(wù)標(biāo)識(shí)符銷(xiāo)毀
#if TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_4_0
Class UIApplicationClass = NSClassFromString(@"UIApplication");
if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
return;
}
if (self.backgroundTaskId != UIBackgroundTaskInvalid) {
UIApplication * app = [UIApplication performSelector:@selector(sharedApplication)];
[app endBackgroundTask:self.backgroundTaskId];
self.backgroundTaskId = UIBackgroundTaskInvalid;
}
#endif
}
- (void)cancel {
@synchronized (self) {
if (self.thread) {
[self performSelector:@selector(cancelInternalAndStop) onThread:self.thread withObject:nil waitUntilDone:NO];
}
else {
[self cancelInternal];
}
}
}
- (void)cancelInternalAndStop {
if (self.isFinished) return;
[self cancelInternal];
}
- (void)cancelInternal {
if (self.isFinished) return;
[super cancel];
if (self.cancelBlock) self.cancelBlock();
if (self.dataTask) {
[self.dataTask cancel];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
});
// As we cancelled the connection, its callback won't be called and thus won't
// maintain the isFinished and isExecuting flags.
if (self.isExecuting) self.executing = NO;
if (!self.isFinished) self.finished = YES;
}
[self reset];
}
- (void)done {
self.finished = YES;
self.executing = NO;
[self reset];
}
/**
* 重置當(dāng)前下載
*/
- (void)reset {
self.cancelBlock = nil;
self.completedBlock = nil;
self.progressBlock = nil;
self.dataTask = nil;
self.imageData = nil;
self.thread = nil;
if (self.ownedSession) {
[self.ownedSession invalidateAndCancel];
self.ownedSession = nil;
}
}
- (void)setFinished:(BOOL)finished {
[self willChangeValueForKey:@"isFinished"];
_finished = finished;
[self didChangeValueForKey:@"isFinished"];
}
- (void)setExecuting:(BOOL)executing {
[self willChangeValueForKey:@"isExecuting"];
_executing = executing;
[self didChangeValueForKey:@"isExecuting"];
}
- (BOOL)isConcurrent {
return YES;
}
#pragma mark -- NSURLSessionDataDelegate
// 告訴delegate已經(jīng)接收到服務(wù)器的初始應(yīng)答, 接下來(lái)準(zhǔn)備數(shù)據(jù)任務(wù)的操作
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
//'304 Not Modified' is an exceptional one
// HTTP狀態(tài)碼:200+正常成功的;300+重定向;400+請(qǐng)求錯(cuò)誤;500+一般時(shí)服務(wù)端的問(wèn)題;304 — 未修改,未按預(yù)期修改文檔。
if (![response respondsToSelector:@selector(statusCode)] || ([((NSHTTPURLResponse *)response) statusCode] < 400 && [((NSHTTPURLResponse *)response) statusCode] != 304)) {
// 預(yù)期文件的長(zhǎng)度,對(duì)于下載來(lái)說(shuō)就是文件的大小
NSInteger expected = response.expectedContentLength > 0 ? (NSInteger)response.expectedContentLength : 0;
self.expectedSize = expected;
if (self.progressBlock) {
self.progressBlock(0, expected);
}
self.imageData = [[NSMutableData alloc] initWithCapacity:expected];
self.response = response;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadReceiveResponseNotification object:self];
});
}
else {
NSUInteger code = [((NSHTTPURLResponse *)response) statusCode];
//This is the case when server returns '304 Not Modified'. It means that remote image is not changed.
//In case of 304 we need just cancel the operation and return cached image from the cache.
// 返回304狀態(tài)碼時(shí),說(shuō)明服務(wù)器上的圖片沒(méi)有改變。僅需要取消操作,返回緩存中的圖片
if (code == 304) {
[self cancelInternal];
} else {
[self.dataTask cancel];
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
});
if (self.completedBlock) {
self.completedBlock(nil, nil, [NSError errorWithDomain:NSURLErrorDomain code:[((NSHTTPURLResponse *)response) statusCode] userInfo:nil], YES);
}
[self done];
}
if (completionHandler) {
completionHandler(NSURLSessionResponseAllow);
}
}
// 告訴delegate已經(jīng)接收到部分?jǐn)?shù)據(jù)
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// 一張圖片的數(shù)據(jù)是追加到imageData后
[self.imageData appendData:data];
if ((self.options & SDWebImageDownloaderProgressiveDownload) && self.expectedSize > 0 && self.completedBlock) {
// The following code is from http://www.cocoaintheshell.com/2011/05/progressive-images-download-imageio/
// Thanks to the author @Nyx0uf
// 已下載圖片數(shù)據(jù)的大小(字節(jié)):Get the total bytes downloaded
const NSInteger totalSize = self.imageData.length;
// 更新數(shù)據(jù)源,須傳遞所有的數(shù)據(jù),而不只是新的字節(jié):Update the data source, we must pass ALL the data, not just the new bytes
CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)self.imageData, NULL);
// 首次獲得圖片的數(shù)據(jù)時(shí),從圖片的數(shù)據(jù)源中獲取圖片相關(guān)信息:方向、高、寬
if (width + height == 0) {
CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
if (properties) {
NSInteger orientationValue = -1;
// 高
CFTypeRef val = CFDictionaryGetValue(properties, kCGImagePropertyPixelHeight);
if (val) CFNumberGetValue(val, kCFNumberLongType, &height);
// 寬
val = CFDictionaryGetValue(properties, kCGImagePropertyPixelWidth);
if (val) CFNumberGetValue(val, kCFNumberLongType, &width);
// 方向
val = CFDictionaryGetValue(properties, kCGImagePropertyOrientation);
if (val) CFNumberGetValue(val, kCFNumberNSIntegerType, &orientationValue);
// CF相關(guān)操作注意手動(dòng)釋放
CFRelease(properties);
// When we draw to Core Graphics, we lose orientation information,
// which means the image below born of initWithCGIImage will be
// oriented incorrectly sometimes. (Unlike the image born of initWithData
// in didCompleteWithError.) So save it here and pass it on later.
// 當(dāng)繪制到Core Graphics時(shí),我們會(huì)丟失方向信息,這意味著有時(shí)候由initWithCGIImage創(chuàng)建的
// 圖片的方向不對(duì)。(不像在connectionDidFinishLoading中用initWithData創(chuàng)建的圖片)
// 所以在這邊我們先保存這個(gè)信息并在后面使用
orientation = [[self class] orientationFromPropertyValue:(orientationValue == -1 ? 1 : orientationValue)];
}
}
// 圖片還未下載完成
if (width + height > 0 && totalSize < self.expectedSize) {
// 使用現(xiàn)有的數(shù)據(jù)創(chuàng)建圖片對(duì)象,如果數(shù)據(jù)中存有多張圖片,則取第一張:Create the image
CGImageRef partialImageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
#ifdef TARGET_OS_IPHONE
// Workaround for iOS anamorphic image
if (partialImageRef) {
const size_t partialHeight = CGImageGetHeight(partialImageRef);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef bmContext = CGBitmapContextCreate(NULL, width, height, 8, width * 4, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(colorSpace);
if (bmContext) {
CGContextDrawImage(bmContext, (CGRect){.origin.x = 0.0f, .origin.y = 0.0f, .size.width = width, .size.height = partialHeight}, partialImageRef);
CGImageRelease(partialImageRef);
partialImageRef = CGBitmapContextCreateImage(bmContext);
CGContextRelease(bmContext);
}
else {
CGImageRelease(partialImageRef);
partialImageRef = nil;
}
}
#endif
if (partialImageRef) {
// 對(duì)圖片進(jìn)行縮放、解碼操作:按道理說(shuō)這里比較耗資源,為啥不添加自動(dòng)釋放池?
UIImage *image = [UIImage imageWithCGImage:partialImageRef scale:1 orientation:orientation];
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
UIImage *scaledImage = [self scaledImageForKey:key image:image];
if (self.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:scaledImage];
}
else {
image = scaledImage;
}
CGImageRelease(partialImageRef);
// 確保下載完的回調(diào)在主線(xiàn)程中執(zhí)行
dispatch_main_sync_safe(^{
if (self.completedBlock) {
self.completedBlock(image, nil, nil, NO);
}
});
}
}
CFRelease(imageSource);
}
if (self.progressBlock) {
self.progressBlock(self.imageData.length, self.expectedSize);
}
}
// 告訴delegate是否把response存儲(chǔ)到緩存中
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
willCacheResponse:(NSCachedURLResponse *)proposedResponse
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler {
responseFromCached = NO; // 如果這個(gè)代理方法被調(diào)用,說(shuō)明緩存中沒(méi)有對(duì)改response進(jìn)行緩存:If this method is called, it means the response wasn't read from cache
NSCachedURLResponse *cachedResponse = proposedResponse;
if (self.request.cachePolicy == NSURLRequestReloadIgnoringLocalCacheData) {
// Prevents caching of responses
cachedResponse = nil;
}
if (completionHandler) {
completionHandler(cachedResponse);
}
}
#pragma mark -- NSURLSessionTaskDelegate
// 告訴delegate,task已經(jīng)完成:即一張圖片已完整下載完這個(gè)task完成時(shí),對(duì)圖片進(jìn)行處理后回調(diào)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
@synchronized(self) {
self.thread = nil;
self.dataTask = nil;
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadStopNotification object:self];
if (!error) {
[[NSNotificationCenter defaultCenter] postNotificationName:SDWebImageDownloadFinishNotification object:self];
}
});
}
if (error) {
if (self.completedBlock) {
self.completedBlock(nil, nil, error, YES);
}
} else {
SDWebImageDownloaderCompletedBlock completionBlock = self.completedBlock;
if (![[NSURLCache sharedURLCache] cachedResponseForRequest:_request]) {
responseFromCached = NO;
}
if (completionBlock) {
if (self.options & SDWebImageDownloaderIgnoreCachedResponse && responseFromCached) {
completionBlock(nil, nil, nil, YES);
} else if (self.imageData) {
UIImage *image = [UIImage sd_imageWithData:self.imageData];
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
image = [self scaledImageForKey:key image:image];
// Gif圖片不要解碼:Do not force decoding animated GIFs
if (!image.images) {
if (self.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:image];
}
}
if (CGSizeEqualToSize(image.size, CGSizeZero)) {
// 空?qǐng)D片
completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Downloaded image has 0 pixels"}], YES);
}
else {
completionBlock(image, self.imageData, nil, YES);
}
} else {
completionBlock(nil, nil, [NSError errorWithDomain:SDWebImageErrorDomain code:0 userInfo:@{NSLocalizedDescriptionKey : @"Image data is nil"}], YES);
}
}
}
self.completionBlock = nil;
[self done];
}
// 告訴delegate, task已收到授權(quán):處理服務(wù)器返回的證書(shū), 需要在該方法中告訴系統(tǒng)是否需要安裝服務(wù)器返回的證書(shū)
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler {
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// 判斷服務(wù)器返回的證書(shū)是否是服務(wù)器信任的
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if (!(self.options & SDWebImageDownloaderAllowInvalidSSLCertificates)) {
// 默認(rèn)處理方式
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
} else {
// 使用指定證書(shū)
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
disposition = NSURLSessionAuthChallengeUseCredential;
}
} else {
if ([challenge previousFailureCount] == 0) {
if (self.credential) {
credential = self.credential;
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
}
// 安裝證書(shū)
if (completionHandler) {
completionHandler(disposition, credential);
}
}
#pragma mark Helper methods
+ (UIImageOrientation)orientationFromPropertyValue:(NSInteger)value {
switch (value) {
case 1:
return UIImageOrientationUp;
case 3:
return UIImageOrientationDown;
case 8:
return UIImageOrientationLeft;
case 6:
return UIImageOrientationRight;
case 2:
return UIImageOrientationUpMirrored;
case 4:
return UIImageOrientationDownMirrored;
case 5:
return UIImageOrientationLeftMirrored;
case 7:
return UIImageOrientationRightMirrored;
default:
return UIImageOrientationUp;
}
}
/**
* 放縮圖片:根據(jù)圖片的scale 或 圖片組 重新計(jì)算返回圖片
*
* @param key key
* @param image image
*
* @return UIImage
*/
- (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image {
return SDScaledImageForKey(key, image);
}
/**
* App在后臺(tái)運(yùn)行,相關(guān)任務(wù)是否繼續(xù)執(zhí)行
*
* @return <#return value description#>
*/
- (BOOL)shouldContinueWhenAppEntersBackground {
return self.options & SDWebImageDownloaderContinueInBackground;
}
@end