SDWebImage探究(十一) —— 深入研究圖片下載流程(五)之SDWebImageDownloadToken和操作對(duì)象的生成和返回

版本記錄

版本號(hào) 時(shí)間
V1.0 2018.02.23

前言

我們做APP,文字和圖片是絕對(duì)不可缺少的元素,特別是圖片一般存儲(chǔ)在圖床里面,一般公司可以委托第三方保存,NB的公司也可以自己存儲(chǔ)圖片,ios有很多圖片加載的第三方框架,其中最優(yōu)秀的莫過于SDWebImage,它幾乎可以滿足你所有的需求,用了好幾年這個(gè)框架,今天想總結(jié)一下。感興趣的可以看其他幾篇。
1. SDWebImage探究(一)
2. SDWebImage探究(二)
3. SDWebImage探究(三)
4. SDWebImage探究(四)
5. SDWebImage探究(五)
6. SDWebImage探究(六) —— 圖片類型判斷深入研究
7. SDWebImage探究(七) —— 深入研究圖片下載流程(一)之有關(guān)option的位移枚舉的說明
8. SDWebImage探究(八) —— 深入研究圖片下載流程(二)之開始下載并返回下載結(jié)果的總的方法
9. SDWebImage探究(九) —— 深入研究圖片下載流程(三)之下載之前的緩存查詢操作
10. SDWebImage探究(十) —— 深入研究圖片下載流程(四)之查詢緩存后的block回調(diào)處理

回顧

上一篇文章我們講述了查詢緩存后的block回調(diào)處理,其中就有SDWebImageDownloadToken這個(gè)token的獲取,這個(gè)token主要作用就是關(guān)聯(lián)每一個(gè)下載操作,可以用于取消一個(gè)下載,這一篇就詳細(xì)的說明一下這個(gè)token是如何生成和返回的。

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock;

這個(gè)方法就是返回token的方法,Creates a SDWebImageDownloader async downloader instance with a given URL,也可以這里理解,根據(jù)給定的url創(chuàng)建一個(gè)SDWebImageDownloader異步的下載器。


SDWebImageDownloadToken的生成及返回

我們先看一下上面返回token方法的實(shí)現(xiàn)。

- (nullable SDWebImageDownloadToken *)downloadImageWithURL:(nullable NSURL *)url
                                                   options:(SDWebImageDownloaderOptions)options
                                                  progress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                                                 completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    __weak SDWebImageDownloader *wself = self;

    return [self addProgressCallback:progressBlock completedBlock:completedBlock forURL:url createCallback:^SDWebImageDownloaderOperation *{
        __strong __typeof (wself) sself = wself;
        NSTimeInterval timeoutInterval = sself.downloadTimeout;
        if (timeoutInterval == 0.0) {
            timeoutInterval = 15.0;
        }

        // In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
        NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
                                                                    cachePolicy:cachePolicy
                                                                timeoutInterval:timeoutInterval];
        
        request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);
        request.HTTPShouldUsePipelining = YES;
        if (sself.headersFilter) {
            request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
        }
        else {
            request.allHTTPHeaderFields = sself.HTTPHeaders;
        }
        SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
        operation.shouldDecompressImages = sself.shouldDecompressImages;
        
        if (sself.urlCredential) {
            operation.credential = sself.urlCredential;
        } else if (sself.username && sself.password) {
            operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
        }
        
        if (options & SDWebImageDownloaderHighPriority) {
            operation.queuePriority = NSOperationQueuePriorityHigh;
        } else if (options & SDWebImageDownloaderLowPriority) {
            operation.queuePriority = NSOperationQueuePriorityLow;
        }

        [sself.downloadQueue addOperation:operation];
        if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
            // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
            [sself.lastAddedOperation addDependency:operation];
            sself.lastAddedOperation = operation;
        }

        return operation;
    }];
}

這里調(diào)用方法addProgressCallback,返回這個(gè)SDWebImageDownloadToken。

1. SDWebImageDownloadToken的生成過程

- (nullable SDWebImageDownloadToken *)addProgressCallback:(SDWebImageDownloaderProgressBlock)progressBlock
                                           completedBlock:(SDWebImageDownloaderCompletedBlock)completedBlock
                                                   forURL:(nullable NSURL *)url
                                           createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback {
    // The URL will be used as the key to the callbacks dictionary so it cannot be nil. If it is nil immediately call the completed block with no image or data.
    if (url == nil) {
        if (completedBlock != nil) {
            completedBlock(nil, nil, nil, NO);
        }
        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.url = url;
        token.downloadOperationCancelToken = downloadOperationCancelToken;
    });

    return token;
}

下面我們就一下來看一下這個(gè)方法的具體實(shí)現(xiàn)。

(a) 容錯(cuò)處理

首先進(jìn)行的還是容錯(cuò)處理,判斷url是否為空,如果為空,就調(diào)用completedBlock(nil, nil, nil, NO);,返回的圖像的數(shù)據(jù)都為空,并return nil。

if (url == nil) {
    if (completedBlock != nil) {
        completedBlock(nil, nil, nil, NO);
    }
    return nil;
}

(b) 設(shè)置阻塞隊(duì)列并在其中生成token

這里使用的是dispatch_barrier_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);,同步阻塞隊(duì)列,與其類似的還有一個(gè)是異步阻塞隊(duì)列,他們的區(qū)別和使用,后面我會(huì)和大家分篇進(jìn)行說明。

這里定義一個(gè)用于存放操作的字典,key就是url

@property (strong, nonatomic, nonnull) NSMutableDictionary<NSURL *, SDWebImageDownloaderOperation *> *URLOperations;

先查看該操作是否存在字典中

SDWebImageDownloaderOperation *operation = self.URLOperations[url];

然后調(diào)用NSOperation中的completionBlock,@property (nullable, copy) void (^completionBlock)(void) API_AVAILABLE(macos(10.6), ios(4.0), watchos(2.0), tvos(9.0));

operation.completionBlock = ^{
    dispatch_barrier_sync(self.barrierQueue, ^{
        SDWebImageDownloaderOperation *soperation = woperation;
        if (!soperation) return;
        if (self.URLOperations[url] == soperation) {
            [self.URLOperations removeObjectForKey:url];
        };
    });
};

在其中做的操作就是判斷該操作是不是在字典中,如果在,就從字典中移除。這里仍然用的是阻塞dispatch_barrier_sync的block。

然后調(diào)用方法,生成token中的一個(gè)屬性token.downloadOperationCancelToken,并賦值。

- (nullable id)addHandlersForProgress:(nullable SDWebImageDownloaderProgressBlock)progressBlock
                            completed:(nullable SDWebImageDownloaderCompletedBlock)completedBlock {
    SDCallbacksDictionary *callbacks = [NSMutableDictionary new];
    if (progressBlock) callbacks[kProgressCallbackKey] = [progressBlock copy];
    if (completedBlock) callbacks[kCompletedCallbackKey] = [completedBlock copy];
    dispatch_barrier_async(self.barrierQueue, ^{
        [self.callbackBlocks addObject:callbacks];
    });
    return callbacks;
}

這里定義了一個(gè)字典,將相關(guān)的block copy后進(jìn)行存儲(chǔ)在字典中,并且將該字典存在一個(gè)可變的數(shù)組callbackBlocks中。

typedef NSMutableDictionary<NSString *, id> SDCallbacksDictionary;

static NSString *const kProgressCallbackKey = @"progress";
static NSString *const kCompletedCallbackKey = @"completed";

@property (strong, nonatomic, nonnull) NSMutableArray<SDCallbacksDictionary *> *callbackBlocks;

SDWebImageDownloaderOperation的生成

生成token的那個(gè)方法中有一個(gè)block createCallback:(SDWebImageDownloaderOperation *(^)(void))createCallback,它是一個(gè)具有返回值的block,大家還記得這句代碼嗎?operation = createCallback();,當(dāng)字典中查找的這個(gè)operation為nil的時(shí)候,就執(zhí)行這個(gè)block返回一個(gè)SDWebImageDownloaderOperation對(duì)象。

下面我們就一起看一下這個(gè)SDWebImageDownloaderOperation的生成過程。

(a)定義下載的超時(shí)

這里定義的是下載的超時(shí),是15s,并且進(jìn)行了處理,如果時(shí)間間隔為0,那么就重新賦值為15s。

NSTimeInterval timeoutInterval = sself.downloadTimeout;
if (timeoutInterval == 0.0) {
    timeoutInterval = 15.0;
}

(b)緩存策略

為了防止?jié)撛诘闹貜?fù)緩存(NSURLCache + SDImageCache),我們禁用圖像請(qǐng)求的緩存。

// In order to prevent from potential duplicate caching (NSURLCache + SDImageCache) we disable the cache for image requests if told otherwise
NSURLRequestCachePolicy cachePolicy = options & SDWebImageDownloaderUseNSURLCache ? NSURLRequestUseProtocolCachePolicy : NSURLRequestReloadIgnoringLocalCacheData;
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url
                                                            cachePolicy:cachePolicy
                                                        timeoutInterval:timeoutInterval];

這里使用一個(gè)三目運(yùn)算符,當(dāng)傳入的options是SDWebImageDownloaderUseNSURLCache時(shí),那么NSURLRequestCachePolicy的實(shí)例化對(duì)象cachePolicy就是NSURLRequestUseProtocolCachePolicy否則就是NSURLRequestReloadIgnoringLocalCacheData。

下面我們看一下這個(gè)NSURLRequestCachePolicy枚舉值

/*!
    @enum NSURLRequestCachePolicy

    @discussion The NSURLRequestCachePolicy enum defines constants that
    can be used to specify the type of interactions that take place with
    the caching system when the URL loading system processes a request.
    Specifically, these constants cover interactions that have to do
    with whether already-existing cache data is returned to satisfy a
    URL load request.
    NSURLRequestCachePolicy枚舉定義了常量可以用來當(dāng)指定URL加載系統(tǒng)處理請(qǐng)求時(shí)的
    緩存系統(tǒng)發(fā)生時(shí)的交互類型 。具體來說,這些常量涵蓋了必須做的交互,用來處理是否返回
    已經(jīng)存在的緩存數(shù)據(jù)以滿足URL加載請(qǐng)求。

    @constant NSURLRequestUseProtocolCachePolicy Specifies that the
    caching logic defined in the protocol implementation, if any, is
    used for a particular URL load request. This is the default policy
    for URL load requests.
    NSURLRequestUseProtocolCachePolicy指定協(xié)議實(shí)現(xiàn)中定義的緩存邏輯(如果有)
    用于特定的URL加載請(qǐng)求。 這是URL加載請(qǐng)求的默認(rèn)策略。

    @constant NSURLRequestReloadIgnoringLocalCacheData Specifies that the
    data for the URL load should be loaded from the origin source. No
    existing local cache data, regardless of its freshness or validity,
    should be used to satisfy a URL load request.
    NSURLRequestReloadIgnoringLocalCacheData指定應(yīng)該從原始源加載URL加載的數(shù)據(jù)。 
    不使用現(xiàn)有的本地緩存數(shù)據(jù)(不管其新鮮度或有效性)滿足URL加載請(qǐng)求。

    @constant NSURLRequestReloadIgnoringLocalAndRemoteCacheData Specifies that
    not only should the local cache data be ignored, but that proxies and
    other intermediates should be instructed to disregard their caches
    so far as the protocol allows.  Unimplemented.
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData指定不僅忽略本地緩存數(shù)據(jù),而且
    應(yīng)該指示代理和其他中轉(zhuǎn)忽略它們的緩存,只要協(xié)議允許。未實(shí)現(xiàn)。

    @constant NSURLRequestReloadIgnoringCacheData Older name for
    NSURLRequestReloadIgnoringLocalCacheData.
    NSURLRequestReloadIgnoringLocalCacheData的較早名稱。

    @constant NSURLRequestReturnCacheDataElseLoad Specifies that the
    existing cache data should be used to satisfy a URL load request,
    regardless of its age or expiration date. However, if there is no
    existing data in the cache corresponding to a URL load request,
    the URL is loaded from the origin source.
    NSURLRequestReturnCacheDataElseLoad指定應(yīng)使用現(xiàn)有緩存數(shù)據(jù)來滿足URL加載請(qǐng)求,
    而不管其緩存時(shí)間或過期日期。 但是,如果沒有與URL加載請(qǐng)求相對(duì)應(yīng)的現(xiàn)有的緩存數(shù)據(jù),
    則URL將從源數(shù)據(jù)源加載。

    @constant NSURLRequestReturnCacheDataDontLoad Specifies that the
    existing cache data should be used to satisfy a URL load request,
    regardless of its age or expiration date. However, if there is no
    existing data in the cache corresponding to a URL load request, no
    attempt is made to load the URL from the origin source, and the
    load is considered to have failed. This constant specifies a
    behavior that is similar to an "offline" mode.
    NSURLRequestReturnCacheDataDontLoad指定應(yīng)使用現(xiàn)有緩存數(shù)據(jù)來滿足URL加載請(qǐng)求,
    而不管其緩存時(shí)間或到期日期。 但是,如果緩存中沒有與URL加載請(qǐng)求相對(duì)應(yīng)的現(xiàn)有數(shù)據(jù),
    則不會(huì)嘗試從源數(shù)據(jù)源加載URL,并且認(rèn)為加載失敗。 此常數(shù)指定與“離線”模式類似的行為。

    @constant NSURLRequestReloadRevalidatingCacheData Specifies that
    the existing cache data may be used provided the origin source
    confirms its validity, otherwise the URL is loaded from the
    origin source.  Unimplemented.
    NSURLRequestReloadRevalidatingCacheData指定可以使用現(xiàn)有的緩存數(shù)據(jù),只要原始源
    確認(rèn)其有效性,否則URL將從原始源加載。未實(shí)現(xiàn)。
*/
typedef NS_ENUM(NSUInteger, NSURLRequestCachePolicy)
{
    NSURLRequestUseProtocolCachePolicy = 0,

    NSURLRequestReloadIgnoringLocalCacheData = 1,
    NSURLRequestReloadIgnoringLocalAndRemoteCacheData = 4, // Unimplemented
    NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,

    NSURLRequestReturnCacheDataElseLoad = 2,
    NSURLRequestReturnCacheDataDontLoad = 3,

    NSURLRequestReloadRevalidatingCacheData = 5, // Unimplemented
};

這里給大家翻譯了一下,看了應(yīng)該好理解了不少吧。

(c)cookies處理

request.HTTPShouldHandleCookies = (options & SDWebImageDownloaderHandleCookies);

只要options是SDWebImageDownloaderHandleCookies,那么就是YES,否則就是NO。

這個(gè)HTTPShouldHandleCookies是NSURLRequest的屬性。

/*!
    @abstract Decide whether default cookie handling will happen for 
    this request (YES if cookies should be sent with and set for this request;
    otherwise NO).
    @discussion The default is YES - in other words, cookies are sent from and 
    stored to the cookie manager by default.
    NOTE: In releases prior to 10.3, this value is ignored
*/
@property BOOL HTTPShouldHandleCookies;

這個(gè)屬性,決定是否對(duì)此請(qǐng)求執(zhí)行默認(rèn)的cookie處理(如果cookie應(yīng)與此請(qǐng)求一起發(fā)送并設(shè)置,則為YES;否則為NO)。默認(rèn)為YES,換句話說,Cookie默認(rèn)情況下是從cookie管理器發(fā)送并存儲(chǔ)到cookie管理器的。 注:在10.3之前的版本中,該值被忽略。

(d)是否使用pipelining

/*!
 @abstract Sets whether the request should not wait for the previous response 
 before transmitting (YES if the receiver should transmit before the previous response is
 received.  NO to wait for the previous response before transmitting)
 @discussion Calling this method with a YES value does not guarantee HTTP 
 pipelining behavior.  This method may have no effect if an HTTP proxy is
 configured, or if the HTTP request uses an unsafe request method (e.g., POST
 requests will not pipeline).  Pipelining behavior also may not begin until
 the second request on a given TCP connection.  There may be other situations
 where pipelining does not occur even though YES was set.
 HTTP 1.1 allows the client to send multiple requests to the server without
 waiting for a response.  Though HTTP 1.1 requires support for pipelining,
 some servers report themselves as being HTTP 1.1 but do not support
 pipelining (disconnecting, sending resources misordered, omitting part of
 a resource, etc.).
 */

設(shè)置請(qǐng)求是否在發(fā)送之前不等待先前的響應(yīng)(如果接收器應(yīng)在接收到前一個(gè)響應(yīng)之前發(fā)送,則為YES;
在發(fā)送之前等待先前的響應(yīng)就設(shè)置為NO)。以YES值調(diào)用此方法并不保證HTTP流水線行為。 如果配置
了HTTP代理,或者HTTP請(qǐng)求使用不安全的請(qǐng)求方法(例如,POST請(qǐng)求不會(huì)流水線),則此方法可能不起
作用。 在給定的TCP連接上的第二個(gè)請(qǐng)求之前,流水線行為也可能不會(huì)開始。 即使設(shè)置了YES,也可能會(huì)
出現(xiàn)流水線不會(huì)發(fā)生的其他情況。 HTTP 1.1允許客戶端向服務(wù)器發(fā)送多個(gè)請(qǐng)求,而無需等待響應(yīng)。 盡管HTTP 1.1
需要流水線支持,但有些服務(wù)器會(huì)將自己報(bào)告為HTTP 1.1,但不支持流水線操作(斷開連接,發(fā)送資源亂序,忽略部分資源等)。
@property BOOL HTTPShouldUsePipelining API_AVAILABLE(macos(10.7), ios(4.0), watchos(2.0), tvos(9.0));

上面給大家翻譯的應(yīng)該很好理解,其實(shí)還可以這么理解,通常默認(rèn)情況下請(qǐng)求和響應(yīng)是順序的, 也就是說請(qǐng)求–>得到響應(yīng)后,再請(qǐng)求。如果將HTTPShouldUsePipelining設(shè)置為YES, 則允許不必等到response, 就可以再次請(qǐng)求。這個(gè)會(huì)很大的提高網(wǎng)絡(luò)請(qǐng)求的效率,但是也可能會(huì)出問題。

因?yàn)榭蛻舳藷o法正確的匹配請(qǐng)求與響應(yīng), 所以這依賴于服務(wù)器必須保證,響應(yīng)的順序與客戶端請(qǐng)求的順序一致,如果服務(wù)器不能保證這一點(diǎn),那可能導(dǎo)致響應(yīng)和請(qǐng)求混亂,具體原理圖如下所示。

(e)HTTP頭區(qū)域的設(shè)置

/*!
    @abstract Sets the HTTP header fields of the receiver to the given
    dictionary.
    @discussion This method replaces all header fields that may have
    existed before this method call. 
    <p>Since HTTP header fields must be string values, each object and
    key in the dictionary passed to this method must answer YES when
    sent an <tt>-isKindOfClass:[NSString class]</tt> message. If either
    the key or value for a key-value pair answers NO when sent this
    message, the key-value pair is skipped.
*/

將接收器的HTTP頭字段設(shè)置為給定的字典。此方法替換此方法調(diào)用之前可能存在的所有頭字段。 
由于HTTP頭字段必須是字符串值,因此傳遞給此方法的字典中的每個(gè)對(duì)象和鍵必須在發(fā)送<tt> -isKindOfClass:[NSString類] </ tt>
消息時(shí)回答YES。 如果鍵值對(duì)的鍵或值在發(fā)送此消息時(shí)回答NO,則會(huì)跳過鍵值對(duì)。
@property (nullable, copy) NSDictionary<NSString *, NSString *> *allHTTPHeaderFields;

這個(gè)屬性返回一個(gè)包含所有接受者HTTP頭區(qū)域的字典。

這里進(jìn)行了判斷

if (sself.headersFilter) {
    request.allHTTPHeaderFields = sself.headersFilter(url, [sself.HTTPHeaders copy]);
}
else {
    request.allHTTPHeaderFields = sself.HTTPHeaders;
}

如果SDWebImageDownloaderHeadersFilterBlock這個(gè)block不為空,那么就返回一個(gè)SDHTTPHeadersDictionary不可變字典,如果為空就返回一個(gè)可變字典@property (strong, nonatomic, nullable) SDHTTPHeadersMutableDictionary *HTTPHeaders。

typedef NSDictionary<NSString *, NSString *> SDHTTPHeadersDictionary;
typedef NSMutableDictionary<NSString *, NSString *> SDHTTPHeadersMutableDictionary;

typedef SDHTTPHeadersDictionary * _Nullable (^SDWebImageDownloaderHeadersFilterBlock)(NSURL * _Nullable url, SDHTTPHeadersDictionary * _Nullable headers);

/**
 * Set filter to pick headers for downloading image HTTP request.
 *
 * This block will be invoked for each downloading image request, returned
 * NSDictionary will be used as headers in corresponding HTTP request.
 */
@property (nonatomic, copy, nullable) SDWebImageDownloaderHeadersFilterBlock headersFilter;

(f)實(shí)例化SDWebImageDownloaderOperation并設(shè)置是否解壓縮圖片

SDWebImageDownloaderOperation *operation = [[sself.operationClass alloc] initWithRequest:request inSession:sself.session options:options];
operation.shouldDecompressImages = sself.shouldDecompressImages;

這里實(shí)例化SDWebImageDownloaderOperation的時(shí)候需要傳入上下文session,如下所示。

// The session in which data tasks will run
@property (strong, nonatomic) NSURLSession *session;

并設(shè)置是否解壓縮圖片

/**
 * Decompressing images that are downloaded and cached can improve performance but can consume lot of memory.
 * Defaults to YES. Set this to NO if you are experiencing a crash due to excessive memory consumption.
 */
@property (assign, nonatomic) BOOL shouldDecompressImages;

解壓下載和緩存的圖像可以提高性能,但會(huì)占用大量內(nèi)存。默認(rèn)設(shè)置為YES。 如果由于內(nèi)存消耗過多而導(dǎo)致崩潰,請(qǐng)將其設(shè)置為NO。

(g)NSURLCredential 身份認(rèn)證

web 服務(wù)可以在返回 http 響應(yīng)時(shí)附帶認(rèn)證要求 challenge,作用是詢問 http 請(qǐng)求的發(fā)起方是誰,這時(shí)發(fā)起方應(yīng)提供正確的用戶名和密碼(即認(rèn)證信息),然后 web 服務(wù)才會(huì)返回真正的 http 響應(yīng)。

下面看一下身份驗(yàn)證

if (sself.urlCredential) {
    operation.credential = sself.urlCredential;
} 
else if (sself.username && sself.password) {
    operation.credential = [NSURLCredential credentialWithUser:sself.username password:sself.password persistence:NSURLCredentialPersistenceForSession];
}

這里,urlCredential屬性定義如下:

/**
 *  Set the default URL credential to be set for request operations.
 */
@property (strong, nonatomic, nullable) NSURLCredential *urlCredential;

操作SDWebImageDownloaderOperationcredential定義如下:

/**
 * The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
 *
 * This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
 */
@property (nonatomic, strong, nullable) NSURLCredential *credential;

這里的邏輯就是這個(gè)驗(yàn)證如果存在就直接通過屬性賦值過去,如果不存在那么就直接利用蘋果的API直接創(chuàng)建。這里創(chuàng)建需要傳入一個(gè)用戶名、密碼還有一個(gè)枚舉值,下面看一下這個(gè)枚舉值。

/*!
    @enum NSURLCredentialPersistence
    @abstract Constants defining how long a credential will be kept around
    @constant NSURLCredentialPersistenceNone This credential won't be saved.
    @constant NSURLCredentialPersistenceForSession This credential will only be stored for this session.
    @constant NSURLCredentialPersistencePermanent This credential will be stored permanently. Note: Whereas in Mac OS X any application can access any credential provided the user gives permission, in iPhone OS an application can access only its own credentials.
    @constant NSURLCredentialPersistenceSynchronizable This credential will be stored permanently. Additionally, this credential will be distributed to other devices based on the owning AppleID.
        Note: Whereas in Mac OS X any application can access any credential provided the user gives permission, on iOS an application can 
        access only its own credentials.
*/

     @abstract常量定義證書將保留多久
     @constant NSURLCredentialPersistenceNone這個(gè)證書不會(huì)被保存。
     @constant NSURLCredentialPersistenceForSession此憑證將僅存儲(chǔ)于此會(huì)話中。
     @constant NSURLCredentialPersistencePermanent這個(gè)憑證將永久存儲(chǔ)。 注意:在Mac OS X中,任何應(yīng)用程序都可以訪問任何憑據(jù),只要用戶授予權(quán)限,在iPhone OS中,應(yīng)用程序只能訪問自己的憑據(jù)。
     @constant NSURLCredentialPersistenceSynchronizable此憑證將永久存儲(chǔ)。 此外,此憑證將根據(jù)擁有的AppleID分配給其他設(shè)備。 注意:在Mac OS X中,任何應(yīng)用程序都可以訪問任何憑據(jù),只要用戶授予權(quán)限即可,一個(gè)iOS程序只能訪問自己的憑據(jù)。

typedef NS_ENUM(NSUInteger, NSURLCredentialPersistence) {
    NSURLCredentialPersistenceNone,
    NSURLCredentialPersistenceForSession,
    NSURLCredentialPersistencePermanent,
    NSURLCredentialPersistenceSynchronizable API_AVAILABLE(macos(10.8), ios(6.0), watchos(2.0), tvos(9.0))
};

(h)隊(duì)列的優(yōu)先級(jí)

這里也設(shè)置了隊(duì)列的優(yōu)先級(jí),如下所示:

if (options & SDWebImageDownloaderHighPriority) {
    operation.queuePriority = NSOperationQueuePriorityHigh;
} else if (options & SDWebImageDownloaderLowPriority) {
    operation.queuePriority = NSOperationQueuePriorityLow;
}

這里,如果options是SDWebImageDownloaderHighPriority,也就是高優(yōu)先級(jí),那么就設(shè)置NSOperation這個(gè)屬性@property NSOperationQueuePriority queuePriority;的優(yōu)先級(jí)為NSOperationQueuePriorityHigh,否則為NSOperationQueuePriorityLow,這里看一下這個(gè)枚舉值。

typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
    NSOperationQueuePriorityVeryLow = -8L,
    NSOperationQueuePriorityLow = -4L,
    NSOperationQueuePriorityNormal = 0,
    NSOperationQueuePriorityHigh = 4,
    NSOperationQueuePriorityVeryHigh = 8
};

這個(gè)很好理解就不多說了。

(i)設(shè)置隊(duì)列的執(zhí)行順序

下面就是將操作加入到隊(duì)列并設(shè)置隊(duì)列的執(zhí)行順序,同時(shí)為LIFO類型的隊(duì)列添加依賴,防止出現(xiàn)錯(cuò)亂。

/**
 * Changes download operations execution order. Default value is `SDWebImageDownloaderFIFOExecutionOrder`.
 */
@property (assign, nonatomic) SDWebImageDownloaderExecutionOrder executionOrder;

@property (strong, nonatomic, nonnull) NSOperationQueue *downloadQueue;
@property (weak, nonatomic, nullable) NSOperation *lastAddedOperation;

[sself.downloadQueue addOperation:operation];
if (sself.executionOrder == SDWebImageDownloaderLIFOExecutionOrder) {
    // Emulate LIFO execution order by systematically adding new operations as last operation's dependency
    [sself.lastAddedOperation addDependency:operation];
    sself.lastAddedOperation = operation;
}

這里隊(duì)列執(zhí)行順序是一個(gè)枚舉,其實(shí)就是隊(duì)列和堆棧兩種隊(duì)列執(zhí)行順序,如下所示:

typedef NS_ENUM(NSInteger, SDWebImageDownloaderExecutionOrder) {
    /**
     * Default value. All download operations will execute in queue style (first-in-first-out).
      默認(rèn)值,所有下載操作都以隊(duì)列的形式執(zhí)行。
     */
    SDWebImageDownloaderFIFOExecutionOrder,

    /**
     * All download operations will execute in stack style (last-in-first-out).
     所有下載操作都以堆棧的形式執(zhí)行。
     */
    SDWebImageDownloaderLIFOExecutionOrder
};

這些都設(shè)置好了,就可以返回對(duì)應(yīng)的操作對(duì)象SDWebImageDownloaderOperation *operation了。

后記

本篇已結(jié)束,后面更精彩~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容