從實現(xiàn)下載來認(rèn)識NSURLConnection

NSURLConnection簡介

  • NSURLConnection是2003年隨著第一版Safari的發(fā)布而發(fā)布的金句,它不單單是一個網(wǎng)絡(luò)請求類,而是指代Foundation框架的URL系統(tǒng)中的一系列關(guān)聯(lián)的組件:NSURLRequest暖释、NSURLResponse、NSURLProtocol稠肘、NSHTTPCookieStorage斥黑、NSURLCredentialStorage以及同名類NSURLConnection。
  • 從iOS9開始埃跷,NSURLConnection中發(fā)送請求的兩個方法已經(jīng)過期(同步請求蕊玷,異步請求),初始化網(wǎng)絡(luò)連接的方法也被設(shè)置為過期,系統(tǒng)不再推薦使用弥雹,蘋果建議使用NSURLSession發(fā)送網(wǎng)絡(luò)請求。

簡單下載

使用NSURLConnection實現(xiàn)簡單下載只需三步

    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos/IMG_3928.MOV"];
    //創(chuàng)建請求對象request
    /*
     1. cachePolicy - 緩存策略
       - NSURLRequestUseProtocolCachePolicy = 0,
         -(常用)默認(rèn)緩存策略延届,若使用requestWithURL方法剪勿,默認(rèn)使用該緩存策略;它會根據(jù)HTTP頭中的信息進(jìn)行緩存處理方庭,服務(wù)器可以在HTTP頭中加入Expires和Cache-Control等來告訴客戶端應(yīng)該施行的緩存策略厕吉。
       - NSURLRequestReloadIgnoringLocalCacheData = 1,
         -(偶爾使用)顧名思義,忽略本地緩存械念,直接加載服務(wù)器數(shù)據(jù)
       - NSURLRequestReturnCacheDataElseLoad = 2,
         -(不用)一直嘗試讀取緩存數(shù)據(jù)头朱,若沒有緩存,才會去請求網(wǎng)絡(luò)龄减,該策略的重大缺陷是無法直到緩存的刷新時機(jī)项钮。
       - NSURLRequestReturnCacheDataDontLoad = 3,
         - (不用)該策略之讀取緩存數(shù)據(jù),無論何時都不會進(jìn)行網(wǎng)絡(luò)請求。
     2. timeoutInterval - 超時時間 一般設(shè)置在15-30秒 AFNetworking中超時時間默認(rèn)60s
     */
    NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:15];
    //連接服務(wù)器烁巫,發(fā)送網(wǎng)絡(luò)請求
    /*
     queue - 這里使用主線程還是子線程由執(zhí)行的代碼塊決定
     該參數(shù)決定block代碼塊在哪個線程上執(zhí)行署隘,若block中有刷新UI的操作,則必須放在主線程上執(zhí)行亚隙;若有一些耗時操作磁餐,則放在子線程上執(zhí)行
     */
    //開始下載
    [NSURLConnection sendAsynchronousRequest:req queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        //下載完成,將數(shù)據(jù)寫入磁盤
        /*
         atomically 原子屬性阿弃,保證線程安全
         */
        [data writeToFile:@"/Users/jsby-yf007/Desktop/test.MOV" atomically:YES];
        NSLog(@"下載完成");
    }];

下載完成后诊霹,可以在命令行通過獲取文件的MD5來驗證文件是否下載完整
圖1

上面代碼在實際開發(fā)中所帶來的問題

1.內(nèi)存會暴漲,出現(xiàn)一個峰值
圖2

出現(xiàn)圖2的情況是因為NSURLConnection下載文件時渣淳,先是將整個文件下載到內(nèi)存脾还,然后再寫入到沙盒,如果文件比較大水由,就會出現(xiàn)內(nèi)存暴漲的情況荠呐。在執(zhí)行

[data writeToFile:@"/Users/jsby-yf007/Desktop/test.MOV" atomically:YES];

這句代碼的時候,data是整個文件的完整數(shù)據(jù)砂客,在文件寫入的過程中泥张,data是存在于內(nèi)存中的,然后一次性寫入到本地鞠值,如此大的數(shù)據(jù)存入內(nèi)存中媚创,當(dāng)然會出現(xiàn)內(nèi)存暴增的情況,當(dāng)寫入完成后彤恶,系統(tǒng)會自動釋放這些內(nèi)存钞钙,所以會出現(xiàn)一個內(nèi)存峰值。本例中的視頻文件只有兩百多兆声离,所以不會出現(xiàn)crash芒炼,但是要是下載一個十幾個G的文件的時候,不用想术徊,肯定crash本刽!

2.沒有下載進(jìn)度以及暫停/繼續(xù)

在實際開發(fā)中,要下載一個很大的文件赠涮,沒有下載進(jìn)度和暫停/繼續(xù)子寓,基本是不可能順利的下載下來的,大大影響用戶體驗笋除!

設(shè)置代理實現(xiàn)下載

使用代理實現(xiàn)進(jìn)度跟進(jìn)

  • 在響應(yīng)方法中獲取到文件總大小斜友。
  • 在接收數(shù)據(jù)的方法中,根據(jù)每次接收到的數(shù)據(jù)長度計算數(shù)據(jù)的總進(jìn)度垃它。
    設(shè)置代理<NSURLConnectionDataDelegate>
//設(shè)置請求路徑url
    NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos/IMG_3928.MOV"];
    //添加請求request
    /*
     1. cachePolicy - 緩存策略
     - NSURLRequestUseProtocolCachePolicy = 0,
     -(常用)默認(rèn)緩存策略鲜屏,若使用requestWithURL方法烹看,默認(rèn)使用該緩存策略;它會根據(jù)HTTP頭中的信息進(jìn)行緩存處理墙歪,服務(wù)器可以在HTTP頭中加入Expires和Cache-Control等來告訴客戶端應(yīng)該施行的緩存策略听系。
     - NSURLRequestReloadIgnoringLocalCacheData = 1,
     -(偶爾使用)顧名思義,忽略本地緩存虹菲,直接加載服務(wù)器數(shù)據(jù)
     - NSURLRequestReturnCacheDataElseLoad = 2,
     -(不用)一直嘗試讀取緩存數(shù)據(jù)靠胜,若沒有緩存,才會去請求網(wǎng)絡(luò)毕源,該策略的重大缺陷是無法直到緩存的刷新時機(jī)浪漠。
     - NSURLRequestReturnCacheDataDontLoad = 3,
     - (不用)該策略之讀取緩存數(shù)據(jù),無論何時都不會進(jìn)行網(wǎng)絡(luò)請求霎褐。
     2. timeoutInterval - 超時時間 一般設(shè)置在15-30秒 AFNetworking中超時時間默認(rèn)60s
     */
    NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:15];
    //3.創(chuàng)建連接并設(shè)置代理
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
    //4.啟動連接
    [conn start];

實現(xiàn)NSURLConnectionDataDelegate的幾個代理方法

#pragma mark - <NSURLConnectionDataDelegate>
//1.接收服務(wù)器的響應(yīng) -- 服務(wù)器的狀態(tài)行&響應(yīng)頭  做一些準(zhǔn)備工作
/*
 NSURLResponse
   - expectedContentLength 服務(wù)器給的預(yù)期數(shù)據(jù)長度 long long 類型
   - suggestedFilename 服務(wù)器建議保存的文件名稱 NSString 類型
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.expectedContentLength = response.expectedContentLength;
    self.currentLength = 0;
}

//2.接收服務(wù)器的數(shù)據(jù) -- 此代理方法可能會調(diào)用多次址愿,因為服務(wù)器返回數(shù)據(jù)是將數(shù)據(jù)拆分成很多段,分段返回給客戶端
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    self.currentLength += data.length;
    float progress = (float)self.currentLength/self.expectedContentLength;
    NSLog(@"下載進(jìn)度%f",progress);
}

//3.所有數(shù)據(jù)接收完成 -- 最后的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"下載完成");
}

//4.下載失敗或出現(xiàn)錯誤
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"出錯了6沉АO煳健!");
}

為實現(xiàn)進(jìn)度跟進(jìn)省艳,聲明兩個變量

/* 文件總大小 */
@property (nonatomic, assign) long long expectedContentLength;
/* 當(dāng)前已下載的文件大小 */
@property (nonatomic, assign) long long currentLength;

以上代碼即可實現(xiàn)下載進(jìn)度跟進(jìn)

使用代理實現(xiàn)數(shù)據(jù)保存

先拼接數(shù)據(jù)娘纷,再寫入

這里我們先聲明兩個變量

/* 保存的目標(biāo)路徑 */
@property (nonatomic, copy) NSString *saveFilePath;
/* 保存的數(shù)據(jù) */
@property (nonatomic, strong) NSMutableData *saveData;

接下來,我們在代理方法中實現(xiàn)數(shù)據(jù)的保存

#pragma mark - <NSURLConnectionDataDelegate>
//1.接收服務(wù)器的響應(yīng) -- 服務(wù)器的狀態(tài)行&響應(yīng)頭  做一些準(zhǔn)備工作
/*
 NSURLResponse
   - expectedContentLength 服務(wù)器給的預(yù)期數(shù)據(jù)長度 long long 類型
   - suggestedFilename 服務(wù)器建議保存的文件名稱 NSString 類型
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.expectedContentLength = response.expectedContentLength;
    self.currentLength = 0;
    //設(shè)置保存的目標(biāo)路徑
    self.saveFilePath = [@"/Users/jsby-yf007/Desktop" stringByAppendingPathComponent:response.suggestedFilename];
}

- (NSMutableData *)saveData
{
    if (!_saveData) {
        _saveData = [[NSMutableData alloc] init];
    }
    return _saveData;
}

//2.接收服務(wù)器的數(shù)據(jù) -- 此代理方法可能會調(diào)用多次跋炕,因為服務(wù)器返回數(shù)據(jù)是將數(shù)據(jù)拆分成很多段赖晶,分段返回給客戶端
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    self.currentLength += data.length;
    float progress = (float)self.currentLength/self.expectedContentLength;
    NSLog(@"下載進(jìn)度%f",progress);
    //將獲取到的數(shù)據(jù)拼接
    [self.saveData appendData:data];
}

//3.所有數(shù)據(jù)接收完成 -- 最后的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    //將數(shù)據(jù)寫入磁盤
    [self.saveData writeToFile:self.saveFilePath atomically:YES];
    //由于saveData為strong類型,使用完之后不會立即釋放辐烂,故遏插,需手動置nil
    self.saveData = nil;
    NSLog(@"下載完成");
}

//4.下載失敗或出現(xiàn)錯誤
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"出錯了!>佬蕖胳嘲!");
}

以上代碼實現(xiàn)了數(shù)據(jù)的保存,但我們發(fā)現(xiàn)扣草,內(nèi)存依然會出現(xiàn)暴增
圖3

由此我們可以推斷胎围,蘋果的sendAsynchronousRequest異步方法內(nèi)部也是通過這種方式來實現(xiàn)文件的保存

邊下載,邊保存

從上面的代碼中我們發(fā)現(xiàn)德召,將數(shù)據(jù)統(tǒng)一拼接好后再寫入依然會出現(xiàn)內(nèi)存暴增的情況,所以汽纤,邊下載上岗,變保存不失為一個比較好的辦法,因為每段數(shù)據(jù)的長度比較小蕴坪,保存完之后肴掷,再釋放這部分內(nèi)存敬锐,顧不會出現(xiàn)內(nèi)存暴增的情況!

1. 使用NSFileHandle實現(xiàn)

#pragma mark - <NSURLConnectionDataDelegate>
//1.接收服務(wù)器的響應(yīng) -- 服務(wù)器的狀態(tài)行&響應(yīng)頭  做一些準(zhǔn)備工作
/*
 NSURLResponse
   - expectedContentLength 服務(wù)器給的預(yù)期數(shù)據(jù)長度 long long 類型
   - suggestedFilename 服務(wù)器建議保存的文件名稱 NSString 類型
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.expectedContentLength = response.expectedContentLength;
    self.currentLength = 0;
    //設(shè)置保存的目標(biāo)路徑
    self.saveFilePath = [@"/Users/jsby-yf007/Desktop" stringByAppendingPathComponent:response.suggestedFilename];
    /*
     在使用NSFileHandle進(jìn)行文件保存的時候呆瞻,若文件已存在台夺,繼續(xù)保存的話,數(shù)據(jù)將繼續(xù)向后拼接痴脾;
     因此颤介,這里采用比較粗暴的方式,直接刪除已存在的文件(實際開發(fā)中不建議這么做)
     在實際開發(fā)中赞赖,我們可以使用NSFileManager對文件進(jìn)行一系列的判斷
     */
    [[NSFileManager defaultManager] removeItemAtPath:self.saveFilePath error:nil];
}

//2.接收服務(wù)器的數(shù)據(jù) -- 此代理方法可能會調(diào)用多次滚朵,因為服務(wù)器返回數(shù)據(jù)是將數(shù)據(jù)拆分成很多段,分段返回給客戶端
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    self.currentLength += data.length;
    float progress = (float)self.currentLength/self.expectedContentLength;
    NSLog(@"下載進(jìn)度%f",progress);
    //將獲取到的數(shù)據(jù)拼接
    [self writeToFileWithData:data];
}

/*
 將數(shù)據(jù)寫入文件 -- 將每段數(shù)據(jù)按順序?qū)懭胛募ㄆ唇訑?shù)據(jù))
 NSFileManager - 文件管理器前域,主要功能:創(chuàng)建目錄辕近,檢查目錄或文件是否存在,刪除目錄或文件匿垄,遍歷目錄移宅。。椿疗。 主要是針對文件的操作  類似于Mac中的Finder
 NSFileHandle - 文件“句柄”(文件指針)對同一文件二進(jìn)制的讀/寫操作
 這里使用 NSFileHandle 來進(jìn)行文件的寫入
 */
- (void)writeToFileWithData:(NSData *)data
{
    /*
     NSFileHandle也是對文件指針的操作
     注意:當(dāng)self.saveFilePath目錄下的文件不存在時漏峰,fileHandleForWritingAtPath方法返回的NSFileHandle對象為nil,因此变丧,我們在使用時芽狗,需要進(jìn)行判斷
     */
    NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.saveFilePath];
    //判斷文件是否存在,NSFileHandle是對文件的操作痒蓬,因此童擎,我們先寫入一段數(shù)據(jù)到磁盤
    if (fp == nil) {
        //如果文件不存,我們先執(zhí)行寫入操作
        [data writeToFile:self.saveFilePath atomically:YES];
    } else {
        //如果文件存在攻晒,將data追加到文件的末尾(拼接)
        /*
         NSFileHandle指針默認(rèn)指向文件的起始位置顾复,當(dāng)我們需要追加數(shù)據(jù)的時候,首先我們需要將文件指針指向文件的末尾鲁捏,這里芯砸,NSFileHandle為我們提供了一個方法seekToEndOfFile,可以將指針移向文件的末尾
         */
        [fp seekToEndOfFile];
        //寫入文件 NSFileHandle 提供了寫入文件的方法
        [fp writeData:data];
        //關(guān)閉 -- 在c語言開發(fā)中给梅,關(guān)于文件的讀假丧、寫操作,都會涉及到文件的打開和關(guān)閉动羽;這里是為了文件數(shù)據(jù)的安全包帚,同時不關(guān)閉打開的文件會占用系統(tǒng)資源
        [fp closeFile];
    }
}

//3.所有數(shù)據(jù)接收完成 -- 最后的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
    NSLog(@"下載完成");
}

//4.下載失敗或出現(xiàn)錯誤
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"出錯了!T讼拧渴邦!");
}

以上是使用NSFileHandle來實現(xiàn)的數(shù)據(jù)寫入操作疯趟,其中,data只是一個局部變量谋梭,使用完即釋放信峻,文件是分段寫入,則不會出現(xiàn)內(nèi)存暴增(圖4)
圖4

2. 使用NSOutputStream實現(xiàn)

NSOutputStream文件輸出流寫入文件的方式是瓮床,每段數(shù)據(jù)會自動向后追加盹舞,不需要像NSFileHandle一樣操作指針來追加數(shù)據(jù)
先聲明一個文件的輸出流對象

/* 文件的輸出流 */
@property (nonatomic, strong) NSOutputStream *fileStream;

代理中的實現(xiàn)

#pragma mark - <NSURLConnectionDataDelegate>
//1.接收服務(wù)器的響應(yīng) -- 服務(wù)器的狀態(tài)行&響應(yīng)頭  做一些準(zhǔn)備工作
/*
 NSURLResponse
   - expectedContentLength 服務(wù)器給的預(yù)期數(shù)據(jù)長度 long long 類型
   - suggestedFilename 服務(wù)器建議保存的文件名稱 NSString 類型
 */
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    self.expectedContentLength = response.expectedContentLength;
    self.currentLength = 0;
    //設(shè)置保存的目標(biāo)路徑
    self.saveFilePath = [@"/Users/jsby-yf007/Desktop" stringByAppendingPathComponent:response.suggestedFilename];
    /*
     在使用NSFileHandle進(jìn)行文件保存的時候,若文件已存在,繼續(xù)保存的話,數(shù)據(jù)將繼續(xù)向后拼接售葡;
     因此棠涮,這里采用比較粗暴的方式,直接刪除已存在的文件(實際開發(fā)中不建議這么做)
     在實際開發(fā)中,我們可以使用NSFileManager對文件進(jìn)行一系列的判斷
     */
    [[NSFileManager defaultManager] removeItemAtPath:self.saveFilePath error:nil];
    
    //創(chuàng)建輸出流 append(追加)
    self.fileStream = [[NSOutputStream alloc] initToFileAtPath:self.saveFilePath append:YES];
    //打開輸出流
    [self.fileStream open];
}

//2.接收服務(wù)器的數(shù)據(jù) -- 此代理方法可能會調(diào)用多次,因為服務(wù)器返回數(shù)據(jù)是將數(shù)據(jù)拆分成很多段,分段返回給客戶端
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
    self.currentLength += data.length;
    float progress = (float)self.currentLength/self.expectedContentLength;
    NSLog(@"下載進(jìn)度%f",progress);
    //將數(shù)據(jù)追加到文件流中
    /*
    第一個參數(shù) uint8_t *類型  數(shù)據(jù)的傳輸都是通過二進(jìn)制流的方式傳輸蓬豁,uint8_t即8位也就是一個ASCII值,該參數(shù)是一個數(shù)組類型菇肃,NSData提供了一個屬性bytes
    第二個參數(shù) 即數(shù)據(jù)長度
     */
    [self.fileStream write:data.bytes maxLength:data.length];
}

//3.所有數(shù)據(jù)接收完成 -- 最后的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//    數(shù)據(jù)流寫入完畢后地粪,關(guān)閉輸出流
    [self.fileStream close];
    NSLog(@"下載完成");
}

//4.下載失敗或出現(xiàn)錯誤
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    NSLog(@"出錯了!K霭蟆技!");
}

NSURLConnection在多線程下的問題

在我們使用NSURLConnection的異步方法時,下載小文件沒有問題斗忌,當(dāng)我們下載大文件時质礼,出現(xiàn)了內(nèi)存暴增的問題,為解決此問題织阳,我們使用了NSURLConnection的代理方法眶蕉,但是我們在使用代理方法時,缺忽略了線程問題唧躲,那么接下來造挽,問題來了,我們知道弄痹,NSURLConnection的代理默認(rèn)是在主線程中執(zhí)行的饭入,但是,為了不阻塞UI肛真,我們需要將執(zhí)行放在子線程上圣拄,查看NSURLConnection的方法,我們發(fā)現(xiàn)毁欣,NSURLConnection提供了一個方法

- (void)setDelegateQueue:(nullable NSOperationQueue*) queue

靈機(jī)一動庇谆,我們可以在創(chuàng)建連接的之后,開始連接之前來設(shè)置一下DelegateQueue將其放入新建隊列中也就是子線程中

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

但是F敬7苟!經(jīng)過測試执解,我們發(fā)現(xiàn)寞肖,問題依然存在,測試發(fā)現(xiàn)衰腌,在下載過程中新蟆,UI的操作會阻塞下載
查看connectionWithRequest:方法的注釋發(fā)現(xiàn)這么一句話

For the connection to work correctly, the calling thread’s run loop must be operating in the default run loop mode.
翻譯:為了使連接正常工作,調(diào)用線程的runloop必須在默認(rèn)runloop模式下運行

也就是說右蕊,我們創(chuàng)建NSURLConnection連接是在哪個模式下運行琼稻,下載任務(wù)就在哪個線程
setDelegateQueue這個方法只是將代理方法中的任務(wù)放入了子線程中執(zhí)行,下載任務(wù)仍然在主線程中饶囚!
接下來要如何解決這個問題呢帕翻??萝风?
我們首先想到的是嘀掸,將整個下載任務(wù)放在子線程中

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"開始了");
        //1.設(shè)置請求路徑url
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos/IMG_3928.MOV"];
        //2.創(chuàng)建請求
        NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:15];
        //3.創(chuàng)建連接并設(shè)置代理
        NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
        //將代理任務(wù)放在子線程中執(zhí)行
        [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
        //4.啟動連接
        [conn start];
        NSLog(@"結(jié)束了");
    });

執(zhí)行完上面的語句后,我們會發(fā)現(xiàn)规惰,下載任務(wù)根本沒執(zhí)行睬塌;這個問題涉及到了一個知識點runloop!每個線程都有一個實際已經(jīng)存在的runloop(運行循環(huán))歇万。但是揩晴,子線程的runloop默認(rèn)不開啟!
那解決方案出來了堕花,我們可以手動來開啟文狱。這里使用coreFoundation框架CFRunLoopRef
1.首先聲明一個CFRunLoopRef

/* 下載所在線程的runloop */
@property (nonatomic, assign) CFRunLoopRef downloadRunLoop;

2.啟動runloop

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"開始了");
        //1.設(shè)置請求路徑url
        NSURL *url = [NSURL URLWithString:@"http://127.0.0.1/videos/IMG_3928.MOV"];
        //2.創(chuàng)建請求
        NSURLRequest *req = [NSURLRequest requestWithURL:url cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:15];
        //3.創(chuàng)建連接并設(shè)置代理
        NSURLConnection *conn = [NSURLConnection connectionWithRequest:req delegate:self];
        //將代理任務(wù)放在子線程中執(zhí)行
        [conn setDelegateQueue:[[NSOperationQueue alloc] init]];
        //4.啟動連接
        [conn start];
        //5.啟動runloop
        /*
         使用coreFoundation框架 中的 CFRunLoopRef
         其中有三個我們需要用到的方法
         CFRunLoopRun            啟動當(dāng)前線程的runloop
         CFRunLoopStop           停止指定線程的runloop
         CFRunLoopGetCurrent  拿到當(dāng)前線程的runloop
         */
        //1.拿到當(dāng)前線程的runloop
        self.downloadRunLoop = CFRunLoopGetCurrent();
        //2.啟動runloop
        CFRunLoopRun();
        NSLog(@"結(jié)束了");
    });

3.停止runloop

//3.所有數(shù)據(jù)接收完成 -- 最后的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//    數(shù)據(jù)流寫入完畢后,關(guān)閉輸出流
    [self.fileStream close];
    //停止下載線程所在的runloop
    CFRunLoopStop(self.downloadRunLoop);
    NSLog(@"下載完成");
}

到此缘挽,使用NSURLConnection實現(xiàn)下載已經(jīng)基本實現(xiàn)瞄崇。
本篇文章旨在學(xué)習(xí)NSURLConnection的原理,如有任何疑問或?qū)懙挠袉栴}的地方壕曼,歡迎大家留言苏研,共同進(jìn)步!下載中的斷點續(xù)傳功能將在下一篇關(guān)于NSURLSession的文章中進(jìn)行詳細(xì)描述腮郊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末摹蘑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子轧飞,更是在濱河造成了極大的恐慌衅鹿,老刑警劉巖撒踪,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異大渤,居然都是意外死亡制妄,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門泵三,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耕捞,“玉大人,你說我怎么就攤上這事烫幕“吵椋” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵较曼,是天一觀的道長磷斧。 經(jīng)常有香客問我,道長诗芜,這世上最難降的妖魔是什么瞳抓? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮伏恐,結(jié)果婚禮上孩哑,老公的妹妹穿的比我還像新娘。我一直安慰自己翠桦,他們只是感情好横蜒,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著销凑,像睡著了一般丛晌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斗幼,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天澎蛛,我揣著相機(jī)與錄音,去河邊找鬼蜕窿。 笑死谋逻,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的桐经。 我是一名探鬼主播毁兆,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼阴挣!你這毒婦竟也來了气堕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茎芭,沒想到半個月后揖膜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡骗爆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年次氨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摘投。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖虹蓄,靈堂內(nèi)的尸體忽然破棺而出犀呼,到底是詐尸還是另有隱情,我是刑警寧澤薇组,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布外臂,位于F島的核電站,受9級特大地震影響律胀,放射性物質(zhì)發(fā)生泄漏宋光。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一炭菌、第九天 我趴在偏房一處隱蔽的房頂上張望罪佳。 院中可真熱鬧,春花似錦黑低、人聲如沸赘艳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕾管。三九已至,卻和暖如春菩暗,著一層夾襖步出監(jiān)牢的瞬間掰曾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工停团, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留旷坦,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓客蹋,卻偏偏與公主長得像塞蹭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子讶坯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

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

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,113評論 1 32
  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,417評論 8 265
  • OC語言基礎(chǔ) 1.類與對象 類方法 OC的類方法只有2種:靜態(tài)方法和實例方法兩種 在OC中番电,只要方法聲明在@int...
    奇異果好補(bǔ)閱讀 4,283評論 0 11
  • iOS面試題目100道 1.線程和進(jìn)程的區(qū)別。 進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨立單位,線程是進(jìn)程的一個實體漱办,...
    有度YouDo閱讀 29,933評論 8 137
  • 監(jiān)獄題材的電影不少这刷,滿足觀眾的獵奇心,正常人也不大可能想體驗下監(jiān)獄生活的娩井,涉及這方面的題材都走暗黑風(fēng)暇屋,要么也是很沉...
    詩影亮畫閱讀 294評論 0 0