SDWebImage探究(十三) —— 深入研究圖片下載流程(七)之NSURLSession中幾個代理的基本用法和關(guān)系

版本記錄

版本號 時間
V1.0 2018.02.24

前言

我們做APP,文字和圖片是絕對不可缺少的元素,特別是圖片一般存儲在圖床里面,一般公司可以委托第三方保存,NB的公司也可以自己存儲圖片,ios有很多圖片加載的第三方框架,其中最優(yōu)秀的莫過于SDWebImage,它幾乎可以滿足你所有的需求,用了好幾年這個框架,今天想總結(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)處理
11. SDWebImage探究(十一) —— 深入研究圖片下載流程(五)之SDWebImageDownloadToken和操作對象的生成和返回
12. SDWebImage探究(十二) —— 深入研究圖片下載流程(六)之下載器到具體下載操作的代理分發(fā)實(shí)現(xiàn)

NSURLSession中的幾種代理

這些代理方法都在NSURLSession中,我們先簡單的看一下該類的功能。

  • 通過URL將數(shù)據(jù)下載到內(nèi)存
  • 通過URL將數(shù)據(jù)下載到文件系統(tǒng)
  • 將數(shù)據(jù)上傳到指定URL
  • 在后臺完成上述功能

NSURLSession是下載的上下文,其中包含了很多下載相關(guān)的代理分發(fā),包括NSURLSessionDelegateNSURLSessionTaskDelegate <NSURLSessionDelegate>NSURLSessionDataDelegate <NSURLSessionTaskDelegate>NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>NSURLSessionStreamDelegate <NSURLSessionTaskDelegate>,它們之間的關(guān)系看繼承就已經(jīng)很清楚了。

它們之間的關(guān)系可以參考下圖:


NSURLSessionDelegate

先看一下API文檔

/*
 * Messages related to the URL session as a whole
   與整個URL session相關(guān)的消息
 */
@protocol NSURLSessionDelegate <NSObject>
@optional

/* The last message a session receives.  A session will only become
 * invalid because of a systemic error or when it has been
 * explicitly invalidated, in which case the error parameter will be nil.
 */
  會話收到的最后一條消息。 一個會話只會因?yàn)橄到y(tǒng)錯誤或者當(dāng)它明確地失效而失效,
  在這種情況下,錯誤參數(shù)將為nil。
- (void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(nullable NSError *)error;

/* If implemented, when a connection level authentication challenge
 * has occurred, this delegate will be given the opportunity to
 * provide authentication credentials to the underlying
 * connection. Some types of authentication will apply to more than
 * one request on a given connection to a server (SSL Server Trust
 * challenges).  If this delegate message is not implemented, the 
 * behavior will be to use the default handling, which may involve user
 * interaction. 
 */
  如果已實(shí)現(xiàn),則在發(fā)生連接級別身份驗(yàn)證質(zhì)詢時,此代理將有機(jī)會向底層連接提供身份驗(yàn)證憑據(jù)。 
  某些類型的身份驗(yàn)證將適用于給定服務(wù)器連接上的多個請求(SSL服務(wù)器信任SSL Server Trust挑戰(zhàn))。 
  如果此代理消息未實(shí)現(xiàn),則行為將使用默認(rèn)處理,這可能涉及用戶交互。
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
                                             completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;

/* If an application has received an
 * -application:handleEventsForBackgroundURLSession:completionHandler:
 * message, the session delegate will receive this message to indicate
 * that all messages previously enqueued for this session have been
 * delivered.  At this time it is safe to invoke the previously stored
 * completion handler, or to begin any internal updates that will
 * result in invoking the completion handler.
 */
  如果應(yīng)用程序收到了-application:handleEventsForBackgroundURLSession:completionHandler:
  消息,則會話代理將收到此消息,指示之前已為此會話入隊(duì)的所有消息已傳遞。 此時,調(diào)用先前存儲的
  完成處理程序或開始任何內(nèi)部更新將導(dǎo)致調(diào)用完成處理程序,這一過程是安全的。
- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session API_AVAILABLE(ios(7.0), watchos(2.0), tvos(9.0)) API_UNAVAILABLE(macos);

@end

NSURLSessionTaskDelegate

下面看一下API

/*
 * Messages related to the operation of a specific task.
   與特定任務(wù)的操作有關(guān)的消息
 */
@protocol NSURLSessionTaskDelegate <NSURLSessionDelegate>
@optional

/*
 * Sent when the system is ready to begin work for a task with a delayed start
 * time set (using the earliestBeginDate property). The completionHandler must
 * be invoked in order for loading to proceed. The disposition provided to the
 * completion handler continues the load with the original request provided to
 * the task, replaces the request with the specified task, or cancels the task.
 * If this delegate is not implemented, loading will proceed with the original
 * request.
 *
 * Recommendation: only implement this delegate if tasks that have the
 * earliestBeginDate property set may become stale and require alteration prior
 * to starting the network load.
 *
 * If a new request is specified, the allowsCellularAccess property from the
 * new request will not be used; the allowsCellularAccess property from the
 * original request will continue to be used.
 *
 * Canceling the task is equivalent to calling the task's cancel method; the
 * URLSession:task:didCompleteWithError: task delegate will be called with error
 * NSURLErrorCancelled.
 */
當(dāng)系統(tǒng)準(zhǔn)備好開始工作時設(shè)置延遲的開始時間(使用earliestBeginDate屬性)時發(fā)送。必須調(diào)用
completionHandler才能繼續(xù)加載。提供給完成處理程序的處置將繼續(xù)使用提供給該任務(wù)的原始請求的
負(fù)載,用指定的任務(wù)替換該請求或取消該任務(wù)。如果這個委托沒有實(shí)現(xiàn),加載將繼續(xù)處理原始請求。

建議:如果具有設(shè)置earliestBeginDate屬性的任務(wù)可能會過時并且需要在開始網(wǎng)絡(luò)加載
之前進(jìn)行更改,那么僅實(shí)現(xiàn)此代理。

如果指定了新請求,則不會使用來自新請求的allowsCellularAccess
屬性;來自原始請求的allowsCellularAccess屬性將繼續(xù)使用。

取消任務(wù)等同于調(diào)用任務(wù)的cancel方法; URLSession:task:didCompleteWithError:任務(wù)代理將被調(diào)用,error為NSURLErrorCancelled。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                        willBeginDelayedRequest:(NSURLRequest *)request
                              completionHandler:(void (^)(NSURLSessionDelayedRequestDisposition disposition, NSURLRequest * _Nullable newRequest))completionHandler
    API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0));

/*
 * Sent when a task cannot start the network loading process because the current
 * network connectivity is not available or sufficient for the task's request.
 *
 * This delegate will be called at most one time per task, and is only called if
 * the waitsForConnectivity property in the NSURLSessionConfiguration has been
 * set to YES.
 *
 * This delegate callback will never be called for background sessions, because
 * the waitForConnectivity property is ignored by those sessions.
 */
當(dāng)任務(wù)無法啟動網(wǎng)絡(luò)加載過程時發(fā)送,因?yàn)楫?dāng)前網(wǎng)絡(luò)連接不可用或不足以滿足任務(wù)請求。

每個任務(wù)最多只能調(diào)用一次該委托,并且只有在NSURLSessionConfiguration中
的waitsForConnectivity屬性設(shè)置為YES時才會調(diào)用該代理。

此代理回調(diào)將永遠(yuǎn)不會被后臺會話調(diào)用,因?yàn)檫@些會話會忽略waitForConnectivity屬性。
- (void)URLSession:(NSURLSession *)session taskIsWaitingForConnectivity:(NSURLSessionTask *)task
    API_AVAILABLE(macos(10.13), ios(11.0), watchos(4.0), tvos(11.0));

/* An HTTP request is attempting to perform a redirection to a different
 * URL. You must invoke the completion routine to allow the
 * redirection, allow the redirection with a modified request, or
 * pass nil to the completionHandler to cause the body of the redirection 
 * response to be delivered as the payload of this request. The default
 * is to follow redirections. 
 *
 * For tasks in background sessions, redirections will always be followed and this method will not be called.
 */
一個HTTP請求正試圖執(zhí)行重定向到一個不同的URL。 您必須調(diào)用完成例程以允許重定向,允許使用修改后
的請求進(jìn)行重定向,或?qū)il傳遞給completionHandler以使重定向響應(yīng)的主體作為此請求的有效內(nèi)容傳遞。 
默認(rèn)是遵循重定向。

對于后臺會話中的任務(wù),將始終遵循重定向,并且不會調(diào)用此方法。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                     willPerformHTTPRedirection:(NSHTTPURLResponse *)response
                                     newRequest:(NSURLRequest *)request
                              completionHandler:(void (^)(NSURLRequest * _Nullable))completionHandler;

/* The task has received a request specific authentication challenge.
 * If this delegate is not implemented, the session specific authentication challenge
 * will *NOT* be called and the behavior will be the same as using the default handling
 * disposition. 
 */
該任務(wù)已收到請求特定的身份驗(yàn)證質(zhì)詢。 如果這個代理沒有被實(shí)現(xiàn),會話特定的認(rèn)證挑戰(zhàn)不會被調(diào)用,
并且行為將與使用默認(rèn)處理處置相同。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                            didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge 
                              completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler;

/* Sent if a task requires a new, unopened body stream.  This may be
 * necessary when authentication has failed for any request that
 * involves a body stream. 
 */
如果任務(wù)需要新的未打開的正文流,則發(fā)送。 
當(dāng)涉及正文流的任何請求的身份驗(yàn)證失敗時,這可能是必需的。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                              needNewBodyStream:(void (^)(NSInputStream * _Nullable bodyStream))completionHandler;

/* Sent periodically to notify the delegate of upload progress.  This
 * information is also available as properties of the task.
 */
定期發(fā)送以通知代理上傳進(jìn)度。 該信息也可作為任務(wù)的屬性。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                                didSendBodyData:(int64_t)bytesSent
                                 totalBytesSent:(int64_t)totalBytesSent
                       totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend;

/*
 * Sent when complete statistics information has been collected for the task.
 */
當(dāng)完成任務(wù)的完整統(tǒng)計信息時發(fā)送。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

/* Sent as the last message related to a specific task.  Error may be
 * nil, which implies that no error occurred and this task is complete. 
 */
作為與特定任務(wù)相關(guān)的最后一條消息發(fā)送。 錯誤可能為nil,
這意味著沒有發(fā)生錯誤,并且此任務(wù)已完成。
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
                           didCompleteWithError:(nullable NSError *)error;

@end

NSURLSessionDataDelegate

下面看一下API文檔

/*
 * Messages related to the operation of a task that delivers data
 * directly to the delegate.
與將數(shù)據(jù)直接傳遞給代理的任務(wù)的操作有關(guān)的消息
 */
@protocol NSURLSessionDataDelegate <NSURLSessionTaskDelegate>
@optional
/* The task has received a response and no further messages will be
 * received until the completion block is called. The disposition
 * allows you to cancel a request or to turn a data task into a
 * download task. This delegate message is optional - if you do not
 * implement it, you can get the response as a property of the task.
 *
 * This method will not be called for background upload tasks (which cannot be converted to download tasks).
 */
該任務(wù)已收到響應(yīng),直到完成塊被調(diào)用不會收到更多消息。 該配置允許您取消請求或?qū)?shù)據(jù)任務(wù)轉(zhuǎn)換
為下載任務(wù)。 此代理消息是可選的 - 如果您沒有實(shí)現(xiàn)它,則可以將響應(yīng)作為任務(wù)的屬性獲取。

此方法不會被后臺上傳任務(wù)(不能轉(zhuǎn)換為下載任務(wù))調(diào)用。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
                                 didReceiveResponse:(NSURLResponse *)response
                                  completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;

/* Notification that a data task has become a download task.  No
 * future messages will be sent to the data task.
 */
通知數(shù)據(jù)任務(wù)已成為下載任務(wù)。 沒有更多的消息將被發(fā)送到數(shù)據(jù)任務(wù)。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
                              didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask;

/*
 * Notification that a data task has become a bidirectional stream
 * task.  No future messages will be sent to the data task.  The newly
 * created streamTask will carry the original request and response as
 * properties.
 *
 * For requests that were pipelined, the stream object will only allow
 * reading, and the object will immediately issue a
 * -URLSession:writeClosedForStream:.  Pipelining can be disabled for
 * all requests in a session, or by the NSURLRequest
 * HTTPShouldUsePipelining property.
 *
 * The underlying connection is no longer considered part of the HTTP
 * connection cache and won't count against the total number of
 * connections per host.
 */
通知數(shù)據(jù)任務(wù)已成為雙向流任務(wù)。 沒有更多的消息將被發(fā)送到數(shù)據(jù)任務(wù)。 新創(chuàng)建的streamTask將作為屬性攜帶原始請求和響應(yīng)。

對于流水線的請求,流對象只允許讀取,而對象將立即發(fā)出-URLSession:writeClosedForStream :。
對于會話中的所有請求,或者NSURLRequest 的HTTPShouldUsePipelining屬性,都可以禁用管道傳輸。

底層連接不再被認(rèn)為是HTTP連接緩存的一部分,并且不會計入每個主機(jī)的連接總數(shù)。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
                                didBecomeStreamTask:(NSURLSessionStreamTask *)streamTask;

/* Sent when data is available for the delegate to consume.  It is
 * assumed that the delegate will retain and not copy the data.  As
 * the data may be discontiguous, you should use 
 * [NSData enumerateByteRangesUsingBlock:] to access it.
 */
數(shù)據(jù)可供代理使用時發(fā)送。 假定代理將保留并不復(fù)制數(shù)據(jù)。 由于數(shù)據(jù)可能不連續(xù),因此應(yīng)該
使用[NSData enumerateByteRangesUsingBlock:]來訪問它。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
                                     didReceiveData:(NSData *)data;

/* Invoke the completion routine with a valid NSCachedURLResponse to
 * allow the resulting data to be cached, or pass nil to prevent
 * caching. Note that there is no guarantee that caching will be
 * attempted for a given resource, and you should not rely on this
 * message to receive the resource data.
 */
用有效的NSCachedURLResponse調(diào)用完成例程以允許緩存結(jié)果數(shù)據(jù),或通過nil來防止緩存。 
請注意,不能保證將為給定資源嘗試緩存,并且不應(yīng)該依賴此消息來接收資源數(shù)據(jù)。
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
                                  willCacheResponse:(NSCachedURLResponse *)proposedResponse 
                                  completionHandler:(void (^)(NSCachedURLResponse * _Nullable cachedResponse))completionHandler;

@end

NSURLSessionDownloadDelegate

下面看一下API

/*
 * Messages related to the operation of a task that writes data to a
 * file and notifies the delegate upon completion.
  與將數(shù)據(jù)寫入文件并在完成時通知代理的任務(wù)的操作有關(guān)的消息
 */
@protocol NSURLSessionDownloadDelegate <NSURLSessionTaskDelegate>

/* Sent when a download task that has completed a download.  The delegate should 
 * copy or move the file at the given location to a new location as it will be 
 * removed when the delegate message returns. URLSession:task:didCompleteWithError: will
 * still be called.
 */
當(dāng)完成下載的下載任務(wù)時發(fā)送。 代理應(yīng)該將指定位置的文件復(fù)制或移動到新位置,因?yàn)榇硐⒎祷?時將被刪除。 URLSession:task:didCompleteWithError:仍然會被調(diào)用。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                              didFinishDownloadingToURL:(NSURL *)location;

@optional
/* Sent periodically to notify the delegate of download progress. */
定期發(fā)送以通知代理下載進(jìn)度
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                                           didWriteData:(int64_t)bytesWritten
                                      totalBytesWritten:(int64_t)totalBytesWritten
                              totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;

/* Sent when a download has been resumed. If a download failed with an
 * error, the -userInfo dictionary of the error will contain an
 * NSURLSessionDownloadTaskResumeData key, whose value is the resume
 * data. 
 */
下載已恢復(fù)時發(fā)送。 如果下載失敗并顯示錯誤,則error的-userInfo字典將包含NSURLSessionDownloadTaskResumeData鍵,其值為恢復(fù)的數(shù)據(jù)。
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
                                      didResumeAtOffset:(int64_t)fileOffset
                                     expectedTotalBytes:(int64_t)expectedTotalBytes;

@end

NSURLSessionStreamDelegate

下面看一下API文檔

@protocol NSURLSessionStreamDelegate <NSURLSessionTaskDelegate>
@optional

/* Indiciates that the read side of a connection has been closed.  Any
 * outstanding reads complete, but future reads will immediately fail.
 * This may be sent even when no reads are in progress. However, when
 * this delegate message is received, there may still be bytes
 * available.  You only know that no more bytes are available when you
 * are able to read until EOF. */
指示連接的讀取端已關(guān)閉。 任何未完成的讀取都會結(jié)束,但將來的讀取將立即失敗。即使沒有
讀取正在進(jìn)行,也可能會發(fā)送此信息。 但是,收到此代理消息時,可能仍有可用字節(jié)。 
只有在能夠讀取EOF時,您才知道沒有更多的字節(jié)可用。
- (void)URLSession:(NSURLSession *)session readClosedForStreamTask:(NSURLSessionStreamTask *)streamTask;

/* Indiciates that the write side of a connection has been closed.
 * Any outstanding writes complete, but future writes will immediately
 * fail.
 */
表示連接的寫入端已關(guān)閉。 任何未完成的寫入都會完成,但將來寫入將立即失敗。
- (void)URLSession:(NSURLSession *)session writeClosedForStreamTask:(NSURLSessionStreamTask *)streamTask;

/* A notification that the system has determined that a better route
 * to the host has been detected (eg, a wi-fi interface becoming
 * available.)  This is a hint to the delegate that it may be
 * desirable to create a new task for subsequent work.  Note that
 * there is no guarantee that the future task will be able to connect
 * to the host, so callers should should be prepared for failure of
 * reads and writes over any new interface. */
系統(tǒng)已經(jīng)確定檢測到到主機(jī)的更好路由的通知(例如,Wi-Fi界面變得可用)。這是對代理的提示,
可能需要為后續(xù)工作創(chuàng)建新任務(wù)。 請注意,不能保證將來的任務(wù)將能夠連接到主機(jī),因此調(diào)用者
應(yīng)該準(zhǔn)備好通過任何新接口進(jìn)行讀寫失敗
- (void)URLSession:(NSURLSession *)session betterRouteDiscoveredForStreamTask:(NSURLSessionStreamTask *)streamTask;

/* The given task has been completed, and unopened NSInputStream and
 * NSOutputStream objects are created from the underlying network
 * connection.  This will only be invoked after all enqueued IO has
 * completed (including any necessary handshakes.)  The streamTask
 * will not receive any further delegate messages.
 */
給定的任務(wù)已完成,未打開的NSInputStream和NSOutputStream對象是從底層網(wǎng)絡(luò)連接創(chuàng)建的。 
這只會在所有入隊(duì)的IO完成(包括任何必要的握手)后才會被調(diào)用。streamTask不會接收到
任何進(jìn)一步的代理消息。
- (void)URLSession:(NSURLSession *)session streamTask:(NSURLSessionStreamTask *)streamTask
                                 didBecomeInputStream:(NSInputStream *)inputStream
                                         outputStream:(NSOutputStream *)outputStream;

@end

參考文章

1. NSURLSession的一些代理方法
2. ios NSURLSession使用說明及后臺工作流程分析

后記

本篇已結(jié)束,主要是介紹NSURLSession這幾個代理的關(guān)系以及它們的簡單用法,資料主要來自蘋果的API,后續(xù)會進(jìn)一步結(jié)合SDWebImage等框架和工程實(shí)踐進(jìn)行深化。喜歡的關(guān)注我,待續(xù)~~~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容