天天看點

iOS之網絡—— NSURLConnection、Runloop、NSURLSession、NSURLSessionDownloadTask

1.NSURLConnection和Runloop(面試)

  • 1.1 涉及知識點

(1)兩種為NSURLConnection設定代理方式的差別

//第一種設定方式:
    //通過該方法設定代理,會自動的發送請求
    // [[NSURLConnection alloc]initWithRequest:request delegate:self];

    //第二種設定方式:
    //設定代理,startImmediately為NO的時候,該方法不會自動發送請求
    NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
    //手動通過代碼的方式來發送請求
    //注意該方法内部會自動的把connect添加到目前線程的RunLoop中在預設模式下執行
    [connect start];
 ```

(2)如何控制代理方法在哪個線程調用

```objc
    //說明:預設情況下,代理方法會在主線程中進行調用(為了友善開發者拿到資料後處理一些重新整理UI的操作不需要考慮到線程間通信)
    //設定代理方法的執行隊列
    [connect setDelegateQueue:[[NSOperationQueue alloc]init]];





<div class="se-preview-section-delimiter"></div>
           

(3)開子線程發送網絡請求的注意點,适用于自動發送網絡請求模式

//使用GCD開啟一個子線程來發送網絡請求
    dispatch_async(dispatch_get_global_queue(, ), ^{
        //使用非自動發送網絡請求模式,發送請求OK
        /*
        //建立NSURLConnection對象,設定代理,暫不發送
      NSURLConnection *connect =  [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];
        //設定代理方法的執行隊列
        [connect setDelegateQueue:[[NSOperationQueue alloc]init]];

        //調用start發送網絡請求
        [connect start];
        */

        //使用自動發送網絡請求模式,發送請求失敗(需要改造代碼)
        //WHY?
        /* 網絡請求發送和資料接收是否成功,和一些因素相關,比如用戶端的網速、伺服器端的查詢速度等等。
           而在子線程中建立的NSURLConnection對象是一個臨時變量,當請求發送完成之後就被釋放了,是以這個時候它的代理方法不會調用用。
           為什麼使用非自動發送網絡請求模式是OK的。
            因為在該模式中,調用了start來開始發送網絡請求,該方法内部會自動将目前的connect作為一個Source添加到目前線程所在的Runloop中
            如果目前線程是子線程(即目前線程的runloop并未建立),那麼該方法内部會預設先建立目前線程的Runloop,設定在runloop的預設模式下運作。
            此時runloop會對這個Connect對象進行強引用,保證了代理方法被調用的前提
         */
        NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self];
        [connect setDelegateQueue:[[NSOperationQueue alloc]init]];
        //建立目前線程的runloop,并開啟runloop
        [[NSRunLoop currentRunLoop] run];
    });

    ```

---





<div class="se-preview-section-delimiter"></div>

###NSURLSession的基本使用
-  涉及知識點

()使用步驟

        使用NSURLSession建立task,然後執行task

()關于task

        a.NSURLSessionTask是一個抽象類,本身不能使用,隻能使用它的子類
        b.NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask

()發送get請求





<div class="se-preview-section-delimiter"></div>

```objc
    //1.建立NSURLSession對象(可以擷取單例對象)
    NSURLSession *session = [NSURLSession sharedSession];

    //2.根據NSURLSession對象建立一個Task

    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/login?username=ss&pwd=ss&type=JSON"];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    //方法參數說明
    /*
    注意:該block是在子線程中調用的,如果拿到資料之後要做一些UI重新整理操作,那麼需要回到主線程重新整理
    第一個參數:需要發送的請求對象
    block:當請求結束拿到伺服器響應的資料時調用block
    block-NSData:該請求的響應體
    block-NSURLResponse:存放本次請求的響應資訊,響應頭,真實類型為NSHTTPURLResponse
    block-NSErroe:請求錯誤資訊
     */
   NSURLSessionDataTask * dataTask =  [session dataTaskWithRequest:request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {

        //拿到響應頭資訊
        NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;

        //4.解析拿到的響應資料
        NSLog(@"%@\n%@",[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding],res.allHeaderFields);
    }];

    //3.執行Task
    //注意:剛建立出來的task預設是挂起狀态的,需要調用該方法來啟動任務(執行任務)
    [dataTask resume];
           

(4)發送get請求的第二種方式

“`

(3)開子線程發送網絡請求的注意點,适用于自動發送網絡請求模式

“`objc

//使用GCD開啟一個子線程來發送網絡請求

dispatch_async(dispatch_get_global_queue(0, 0), ^{

//使用非自動發送網絡請求模式,發送請求OK

/*

//建立NSURLConnection對象,設定代理,暫不發送

NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:NO];

//設定代理方法的執行隊列

[connect setDelegateQueue:[[NSOperationQueue alloc]init]];

//調用start發送網絡請求
    [connect start];
    */

    //使用自動發送網絡請求模式,發送請求失敗(需要改造代碼)
    //WHY?
    /*01 網絡請求發送和資料接收是否成功,和一些因素相關,比如用戶端的網速、伺服器端的查詢速度等等。
      02 而在子線程中建立的NSURLConnection對象是一個臨時變量,當請求發送完成之後就被釋放了,是以這個時候它的代理方法不會調用用。
      03 為什麼使用非自動發送網絡請求模式是OK的。
        因為在該模式中,調用了start來開始發送網絡請求,該方法内部會自動将目前的connect作為一個Source添加到目前線程所在的Runloop中
        如果目前線程是子線程(即目前線程的runloop并未建立),那麼該方法内部會預設先建立目前線程的Runloop,設定在runloop的預設模式下運作。
        此時runloop會對這個Connect對象進行強引用,保證了代理方法被調用的前提
     */
    NSURLConnection *connect = [[NSURLConnection alloc]initWithRequest:request delegate:self];
    [connect setDelegateQueue:[[NSOperationQueue alloc]init]];
    //建立目前線程的runloop,并開啟runloop
    [[NSRunLoop currentRunLoop] run];
});

```
           

2.NSURLSession的基本使用

  • 2.1 涉及知識點

(1)使用步驟

使用NSURLSession建立task,然後執行task
           

(2)關于task

a.NSURLSessionTask是一個抽象類,本身不能使用,隻能使用它的子類
    b.NSURLSessionDataTask\NSURLSessionUploadTask\NSURLSessionDownloadTask
           

(3)發送get請求

3.NSURLSession下載下傳檔案-代理

  • 3.1 涉及知識點

(1)建立NSURLSession對象,設定代理(預設配置)

//1.建立NSURLSession,并設定代理
    /*
     第一個參數:session對象的全局配置設定,一般使用預設配置就可以
     第二個參數:誰成為session對象的代理
     第三個參數:代理方法在哪個隊列中執行(在哪個線程中調用),如果是主隊列那麼在主線程中執行,如果是非主隊列,那麼在子線程中執行
     */
    NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
           

(2)根據Session對象建立一個NSURLSessionDataTask任務(post和get選擇)

//建立task
NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_01.png"];

//注意:如果要發送POST請求,那麼請使用dataTaskWithRequest,設定一些請求頭資訊
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url];
           

(3)執行任務(其它方法,如暫停、取消等)

//啟動task
    //[dataTask resume];
    //其它方法,如取消任務,暫停任務等
    //[dataTask cancel];
    //[dataTask suspend];
           

(4)遵守代理協定,實作代理方法(3個相關的代理方法)

/*
 1.當接收到伺服器響應的時候調用
     session:發送請求的session對象
     dataTask:根據NSURLSession建立的task任務
     response:伺服器響應資訊(響應頭)
     completionHandler:通過該block回調,告訴伺服器端是否接收傳回的資料
 */
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveResponse:(nonnull NSURLResponse *)response completionHandler:(nonnull void (^)(NSURLSessionResponseDisposition))completionHandler

/*
 2.當接收到伺服器傳回的資料時調用
 該方法可能會被調用多次
 */
-(void)URLSession:(nonnull NSURLSession *)session dataTask:(nonnull NSURLSessionDataTask *)dataTask didReceiveData:(nonnull NSData *)data

/*
 3.當請求完成之後調用該方法
 不論是請求成功還是請求失敗都調用該方法,如果請求失敗,那麼error對象有值,否則那麼error對象為空
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
           

(5)當接收到伺服器響應的時候,告訴伺服器接收資料(調用block)

//預設情況下,當接收到伺服器響應之後,伺服器認為用戶端不需要接收資料,是以後面的代理方法不會調用
    //如果需要繼續接收伺服器傳回的資料,那麼需要調用block,并傳入對應的政策

    /*
        NSURLSessionResponseCancel = 0, 取消任務
        NSURLSessionResponseAllow = 1,  接收任務
        NSURLSessionResponseBecomeDownload = 2, 轉變成下載下傳
        NSURLSessionResponseBecomeStream NS_ENUM_AVAILABLE(10_11, 9_0) = 3, 轉變成流
    */

    completionHandler(NSURLSessionResponseAllow);
           

4.NSURLSessionDownloadTask實作大檔案下載下傳

  • 4.1 涉及知識點

(1)使用NSURLSession和NSURLSessionDownload可以很友善的實作檔案下載下傳操作

/*
     第一個參數:要下載下傳檔案的url路徑
     第二個參數:當接收完伺服器傳回的資料之後調用該block
     location:下載下傳的檔案的儲存位址(預設是存儲在沙盒中tmp檔案夾下面,随時會被删除)
     response:伺服器響應資訊,響應頭
     error:該請求的錯誤資訊
     */
    //說明:downloadTaskWithURL方法已經實作了在下載下傳檔案資料的過程中邊下載下傳檔案資料,邊寫入到沙盒檔案的操作
    NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL * __nullable location, NSURLResponse * __nullable response, NSError * __nullable error)
           

(2)downloadTaskWithURL内部預設已經實作了變下載下傳邊寫入操作,是以不用開發人員擔心記憶體問題

(3)檔案下載下傳後預設儲存在tmp檔案目錄,需要開發人員手動的剪切到合适的沙盒目錄

(4)缺點:沒有辦法監控下載下傳進度

5.使用NSURLSessionDownloadTask實作大檔案下載下傳-監聽下載下傳進度

  • 5.1 涉及知識點

(1)建立NSURLSession并設定代理,通過NSURLSessionDownloadTask并以代理的方式來完成大檔案的下載下傳

//1.建立NSULRSession,設定代理
    self.session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];

    //2.建立task
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];
    self.downloadTask = [self.session downloadTaskWithURL:url];

    //3.執行task
    [self.downloadTask resume];
           

(2)常用代理方法的說明

/*
 1.當接收到下載下傳資料的時候調用,可以在該方法中監聽檔案下載下傳的進度
 該方法會被調用多次
 totalBytesWritten:已經寫入到檔案中的資料大小
 totalBytesExpectedToWrite:目前檔案的總大小
 bytesWritten:本次下載下傳的檔案資料大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
/*
 2.恢複下載下傳的時候調用該方法
 fileOffset:恢複之後,要從檔案的什麼地方開發下載下傳
 expectedTotalBytes:該檔案資料的總大小
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes
/*
 3.下載下傳完成之後調用該方法
 */
-(void)URLSession:(nonnull NSURLSession *)session downloadTask:(nonnull NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(nonnull NSURL *)location
/*
 4.請求完成之後調用
 如果請求失敗,那麼error有值
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didCompleteWithError:(nullable NSError *)error
           

(3)實作斷點下載下傳相關代碼

//如果任務,取消了那麼以後就不能恢複了
    //    [self.downloadTask cancel];

    //如果采取這種方式來取消任務,那麼該方法會通過resumeData儲存目前檔案的下載下傳資訊
    //隻要有了這份資訊,以後就可以通過這些資訊來恢複下載下傳
    [self.downloadTask cancelByProducingResumeData:^(NSData * nullable resumeData) {
        self.resumeData = resumeData;
    }];

    -----------
    //繼續下載下傳
    //首先通過之前儲存的resumeData資訊,建立一個下載下傳任務
    self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];

     [self.downloadTask resume];
           

(4)計算目前下載下傳進度

//擷取檔案下載下傳進度
    self.progress.progress =  * totalBytesWritten/totalBytesExpectedToWrite;
 ```

()局限性

     如果使用者點選暫停之後退出程式,那麼需要把恢複下載下傳的資料寫一份到沙盒,代碼複雜度更
     如果使用者在下載下傳中途未儲存恢複下載下傳資料即退出程式,則不具備可操作性

---

###使用NSURLSessionDataTask實作大檔案離線斷點下載下傳(完整)
-  涉及知識點

()關于NSOutputStream的使用
```objc
    // 建立一個輸入流,資料追加到檔案的屁股上
    //把資料寫入到指定的檔案位址,如果目前檔案不存在,則會自動建立
    NSOutputStream *stream = [[NSOutputStream alloc]initWithURL:[NSURL fileURLWithPath:[self fullPath]] append:YES];

    // 打開流
    [stream open];

    // 寫入流資料
    [stream write:data.bytes maxLength:data.length];

    //當不需要的時候應該關閉流
    [stream close];




<div class="se-preview-section-delimiter"></div>
           

(2)關于網絡請求請求頭的設定(可以設定請求下載下傳檔案的某一部分)

//1. 設定請求對象
    //1.1 建立請求路徑
    NSURL *url = [NSURL URLWithString:@"http://120.25.226.186:32812/resources/videos/minion_01.mp4"];

    //1.2 建立可變請求對象
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

    //1.3 拿到目前檔案的殘留資料大小
    self.currentContentLength = [self FileSize];

    //1.4 告訴伺服器從哪個地方開始下載下傳檔案資料
    NSString *range = [NSString stringWithFormat:@"bytes=%zd-",self.currentContentLength];
    NSLog(@"%@",range);

    //1.5 設定請求頭
    [request setValue:range forHTTPHeaderField:@"Range"];




<div class="se-preview-section-delimiter"></div>
           

(3)NSURLSession對象的釋放

-(void)dealloc
{
    //在最後的時候應該把session釋放,以免造成記憶體洩露
    //    NSURLSession設定過代理後,需要在最後(比如控制器銷毀的時候)調用session的invalidateAndCancel或者resetWithCompletionHandler,才不會有記憶體洩露
    //    [self.session invalidateAndCancel];
    [self.session resetWithCompletionHandler:^{

        NSLog(@"釋放---");
    }];
}




<div class="se-preview-section-delimiter"></div>

           

(4)優化部分

01 關于檔案下載下傳進度的實時更新
    02 方法的獨立與抽取
           

7.NSURLSession實作檔案上傳

  • 7.1 涉及知識點

(1)實作檔案上傳的方法

/*
     第一個參數:請求對象
     第二個參數:請求體(要上傳的檔案資料)
     block回調:
     NSData:響應體
     NSURLResponse:響應頭
     NSError:請求的錯誤資訊
     */
    NSURLSessionUploadTask *uploadTask =  [session uploadTaskWithRequest:request fromData:data completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error)




<div class="se-preview-section-delimiter"></div>
           

(2)設定代理,在代理方法中監聽檔案上傳進度

/*
 調用該方法上傳檔案資料
 如果檔案資料很大,那麼該方法會被調用多次
 參數說明:
     totalBytesSent:已經上傳的檔案資料的大小
     totalBytesExpectedToSend:檔案的總大小
 */
-(void)URLSession:(nonnull NSURLSession *)session task:(nonnull NSURLSessionTask *)task didSendBodyData:(int64_t)bytesSent totalBytesSent:(int64_t)totalBytesSent totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
{
    NSLog(@"%.2f", * totalBytesSent/totalBytesExpectedToSend);
}




<div class="se-preview-section-delimiter"></div>

           

(3)關于NSURLSessionConfiguration相關

01 作用:可以統一配置NSURLSession,如請求逾時等
02 建立的方式和使用
           
//建立配置的三種方式
+ (NSURLSessionConfiguration *)defaultSessionConfiguration;
+ (NSURLSessionConfiguration *)ephemeralSessionConfiguration;
+ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier NS_AVAILABLE(_10, _0);

//統一配置NSURLSession
-(NSURLSession *)session
{
    if (_session == nil) {

        //建立NSURLSessionConfiguration
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];

        //設定請求逾時為10秒鐘
        config.timeoutIntervalForRequest = ;

        //在蜂窩網絡情況下是否繼續請求(上傳或下載下傳)
        config.allowsCellularAccess = NO;

        _session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:[NSOperationQueue mainQueue]];
    }
    return _session;
}





<div class="se-preview-section-delimiter"></div>
           

8.AFN架構基本使用

  • 8.1 AFN内部結構
AFN結構體
    - NSURLConnection
        + AFURLConnectionOperation
        + AFHTTPRequestOperation
        + AFHTTPRequestOperationManager(封裝了常用的 HTTP 方法)
            * 屬性
                * baseURL :AFN建議開發者針對 AFHTTPRequestOperationManager 自定義個一個單例子類,設定 baseURL, 所有的網絡通路,都隻使用相對路徑即可
                * requestSerializer :請求資料格式/預設是二進制的 HTTP
                * responseSerializer :響應的資料格式/預設是 JSON 格式
                * operationQueue
                * reachabilityManager :網絡連接配接管理器
            * 方法
                * manager :友善建立管理器的類方法
                * HTTPRequestOperationWithRequest :在通路伺服器時,如果要告訴伺服器一些附加資訊,都需要在 Request 中設定
                * GET
                * POST

    - NSURLSession
        + AFURLSessionManager
        + AFHTTPSessionManager(封裝了常用的 HTTP 方法)
            * GET
            * POST
            * UIKit + AFNetworking 分類
            * NSProgress :利用KVO

    - 半自動的序列化&反序列化的功能
        + AFURLRequestSerialization :請求的資料格式/預設是二進制的
        + AFURLResponseSerialization :響應的資料格式/預設是JSON格式
    - 附加功能
        + 安全政策
            * HTTPS
            * AFSecurityPolicy
        + 網絡檢測
            * 對蘋果的網絡連接配接檢測做了一個封裝
            * AFNetworkReachabilityManager

建議:
可以學習下AFN對 UIKit 做了一些分類, 對自己能力提升是非常有幫助的




<div class="se-preview-section-delimiter"></div>
           
  • 8.2 AFN的基本使用

(1)發送GET請求的兩種方式(POST同)

-(void)get1
{
    //1.建立AFHTTPRequestOperationManager管理者
    //AFHTTPRequestOperationManager内部是基于NSURLConnection實作的
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];

    //2.發送請求
    /*
     http://120.25.226.186:32812/login?username=ee&pwd=ee&type=JSON
     第一個參數:NSString類型的請求路徑,AFN内部會自動将該路徑包裝為一個url并建立請求對象
     第二個參數:請求參數,以字典的方式傳遞,AFN内部會判斷目前是POST請求還是GET請求,以選擇直接拼接還是轉換為NSData放到請求體中傳遞
     第三個參數:請求成功之後回調Block
     第四個參數:請求失敗回調Block
     */

    NSDictionary *param = @{
                            @"username":@"520it",
                            @"pwd":@"520it"
                            };

    //注意:字元串中不能包含空格
    [manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(AFHTTPRequestOperation * _Nonnull operation, id  _Nonnull responseObject) {

        NSLog(@"請求成功---%@",responseObject);

    } failure:^(AFHTTPRequestOperation * _Nonnull operation, NSError * _Nonnull error) {
            NSLog(@"失敗---%@",error);
    }];
}

-(void)get2
{
    //1.建立AFHTTPSessionManager管理者
    //AFHTTPSessionManager内部是基于NSURLSession實作的
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

    //2.發送請求
    NSDictionary *param = @{
                            @"username":@"520it",
                            @"pwd":@"520it"
                            };

    //注意:responseObject:請求成功傳回的響應結果(AFN内部已經把響應體轉換為OC對象,通常是字典或數組)
    [manager GET:@"http://120.25.226.186:32812/login" parameters:param success:^(NSURLSessionDataTask * _Nonnull task, id  _Nonnull responseObject) {
            NSLog(@"請求成功---%@",[responseObject class]);

    } failure:^(NSURLSessionDataTask * _Nonnull task, NSError * _Nonnull error) {
        NSLog(@"失敗---%@",error);
    }];
}




<div class="se-preview-section-delimiter"></div>

           

(2)使用AFN下載下傳檔案

-(void)download
{
    //1.建立一個管理者
    AFHTTPSessionManager *manage  = [AFHTTPSessionManager manager];

    //2.下載下傳檔案
    /*
     第一個參數:請求對象
     第二個參數:下載下傳進度
     第三個參數:block回調,需要傳回一個url位址,用來告訴AFN下載下傳檔案的目标位址
         targetPath:AFN内部下載下傳檔案存儲的位址,tmp檔案夾下
         response:請求的響應頭
         傳回值:檔案應該剪切到什麼地方
     第四個參數:block回調,當檔案下載下傳完成之後調用
        response:響應頭
        filePath:檔案存儲在沙盒的位址 == 第三個參數中block的傳回值
        error:錯誤資訊
     */

    //2.1 建立請求對象
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://120.25.226.186:32812/resources/images/minion_02.png"]];

    //2.2 建立下載下傳進度,并監聽
    NSProgress *progress = nil;

    NSURLSessionDownloadTask *downloadTask = [manage downloadTaskWithRequest:request progress:&progress destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {

        NSString *caches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];

        //拼接檔案全路徑
        NSString *fullpath = [caches stringByAppendingPathComponent:response.suggestedFilename];
        NSURL *filePathUrl = [NSURL fileURLWithPath:fullpath];
        return filePathUrl;

    } completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nonnull filePath, NSError * _Nonnull error) {

        NSLog(@"檔案下載下傳完畢---%@",filePath);
    }];

    //2.3 使用KVO監聽下載下傳進度
    [progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:nil];

    //3.啟動任務
    [downloadTask resume];
}

//擷取并計算目前檔案的下載下傳進度
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(NSProgress *)progress change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
    NSLog(@"%zd--%zd--%f",progress.completedUnitCount,progress.totalUnitCount, * progress.completedUnitCount/progress.totalUnitCount);
}




<div class="se-preview-section-delimiter"></div>

           

9.Cocoapods的安裝

先更新Gem
    sudo gem update --system
切換cocoapods的資料源
    【先删除,再添加,檢視】
    gem sources --remove https://rubygems.org/
    gem sources -a https://ruby.taobao.org/
    gem sources -l
安裝cocoapods
    sudo gem install cocoapods
将Podspec檔案托管位址從github切換到國内的oschina
    【先删除,再添加,再更新】
    pod repo remove master
    pod repo add master http://git.oschina.net/akuandev/Specs.git
    pod repo add master https://gitcafe.com/akuandev/Specs.git
    pod repo update
設定pod倉庫
    pod setup
測試
    【如果有版本号,則說明已經安裝成功】
    pod --version
利用cocoapods來安裝第三方架構
     進入要安裝架構的項目的.xcodeproj同級檔案夾
     在該檔案夾中建立一個檔案Podfile
     在檔案中告訴cocoapods需要安裝的架構資訊
        a.該架構支援的平台
        b.适用的iOS版本
        c.架構的名稱
        d.架構的版本
安裝
pod install --no-repo-update
pod update --no-repo-update
           
//如果任務,取消了那麼以後就不能恢複了
    //    [self.downloadTask cancel];

    //如果采取這種方式來取消任務,那麼該方法會通過resumeData儲存目前檔案的下載下傳資訊
    //隻要有了這份資訊,以後就可以通過這些資訊來恢複下載下傳
    [self.downloadTask cancelByProducingResumeData:^(NSData * nullable resumeData) {
        self.resumeData = resumeData;
    }];

    -----------
    //繼續下載下傳
    //首先通過之前儲存的resumeData資訊,建立一個下載下傳任務
    self.downloadTask = [self.session downloadTaskWithResumeData:self.resumeData];

     [self.downloadTask resume];
           

(4)計算目前下載下傳進度

objc //擷取檔案下載下傳進度 self.progress.progress = 1.0 * totalBytesWritten/totalBytesExpectedToWrite;

(5)局限性

01 如果使用者點選暫停之後退出程式,那麼需要把恢複下載下傳的資料寫一份到沙盒,代碼複雜度更
02 如果使用者在下載下傳中途未儲存恢複下載下傳資料即退出程式,則不具備可操作性
           

繼續閱讀