NSURLConnection文件下載和NSRunLoopCommonModes

傳智播客視頻學(xué)習(xí)筆記+個人總結(jié)
發(fā)送請求的步驟:
1.設(shè)置url
2.設(shè)置request
3.發(fā)送請求,同步or異步

使用同步方法下載文件

在主線程調(diào)用同步方法御吞,一直在等待服務(wù)器返回數(shù)據(jù)麦箍,代碼會卡住,如果服務(wù)器陶珠,沒有返回數(shù)據(jù)挟裂,那么在主線程UI會卡住不能繼續(xù)執(zhí)行操作。有返回值NSData

//1.url
NSString *urlstr = @"xxxx";
urlstr = [urlstr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlstr];
//2.request
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//3.connection
NSLog(@"start");
NSData *data=[NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
//一直在等待服務(wù)器返回數(shù)據(jù)
NSLog(@"--%d--",data.length);
  • 注意:同步的連接會阻塞調(diào)用它的線程,不一定是主線程

使用異步方法下載文件

沒有返回值

    //1.url
    NSString *urlstr = @"xxxx";
    urlstr = [urlstr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlstr];
    
    //2.request
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.connection
    NSLog(@"start");
    //使用這個方法會有內(nèi)存峰值
    //queue參數(shù)是指定block的執(zhí)行隊列
    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
        //將文件寫入磁盤
        //內(nèi)存峰值:下載完成瞬間再把文件寫入磁盤揍诽。下載文件有多大诀蓉,NSData就會占用多大的內(nèi)存。
        [data writeToFile:@"XXXXXX" atomically:YES];
        NSLog(@"finsh");
    }];
  • 開一條新的線程去發(fā)送請求暑脆,主線程繼續(xù)往下走渠啤,當(dāng)拿到服務(wù)器的返回數(shù)據(jù)的數(shù)據(jù)的時候再回調(diào)block,執(zhí)行block代碼段添吗。不會卡住主線程沥曹。
  • 關(guān)于queue參數(shù),隊列的作用:An NSOperationQueue upon which the handler block will be dispatched.決定這個block操作放在哪個線程執(zhí)行碟联。
    如果是NSOperationQueue *queue=[[NSOperationQueue alloc]init];默認(rèn)是異步執(zhí)行妓美。(接下來也會有提及)
    如果是主隊列mainqueue,那么在子線程發(fā)送請求成功并獲取到服務(wù)器的數(shù)據(jù)響應(yīng)鲤孵。就可以回到主線程中解析數(shù)據(jù)壶栋,并刷新UI界面。
  • 如果有刷新UI界面的操作應(yīng)該放在主線程執(zhí)行普监,不能放在子線程贵试。

存在的問題:
1.沒有下載進度琉兜,會影響用戶體驗
2.使用異步方法,下載完成執(zhí)行回調(diào)時再把文件寫入磁盤锡移,會造成內(nèi)存峰值的問題呕童。下載文件有多大,NSData就會占用多大的內(nèi)存

使用代理

問題1的解決辦法:使用代理NSURLConnectionDataDelegate
1.在響應(yīng)方法中獲得文件總大小
2.每次接收到數(shù)據(jù)淆珊,計算數(shù)據(jù)的總長度夺饲,和總大小相比,得出百分比

//要下載文件的總長度
@property (nonatomic , assign)long long expectedContentLength;
//當(dāng)前下載的長度
@property (nonatomic , assign)long long currentLength;

    //1.url
    NSString *urlstr = @"xxxx";
    urlstr = [urlstr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlstr];
    
    //2.request
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.connection施符,不做設(shè)置的話是在主線程中執(zhí)行之后的下載
    NSLog(@"start");
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
    
    //設(shè)置代理工作的操作隊列
    [conn setDelegateQueue:[[NSOperationQueue alloc]init]];
    
    //4.啟動連接
    [conn start];
//代理
//1.接收到服務(wù)器的響應(yīng) :狀態(tài)行和響應(yīng)頭,用來做一些準(zhǔn)備工作
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"%@",response);//response里面有狀態(tài)行和響應(yīng)頭
    //記錄文件總大小
    self.expectedContentLength = response.expectedContentLength;
    self.currentLength = 0;
}

//2.接收到服務(wù)器的數(shù)據(jù) :此方法可能會執(zhí)行多次往声,因為會接收到多個data
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    NSLog(@"接收到的數(shù)據(jù)長度%tu",data.length);
    //計算百分比
    self.currentLength += data.length;
    
    float progress = (float)self.currentLength / self.expectedContentLength;
    NSLog(@"%f",progress);    
}

//3.所有數(shù)據(jù)加載完成 : 所有數(shù)據(jù)傳輸完畢之后被調(diào)用,是最后一個的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
}

//4.下載失敗或者錯誤
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
}

  • 要下載的文件總大小在服務(wù)器返回的響應(yīng)頭里面可以拿到
NSDictionary *headerDic = response.allHeaderFields; 
self.fileLength = [[headerDic objectForKey:@"Content-Length"] intValue];

或者

response.expectedContentLength 要下載的文件總大小
response.suggestedFilename 獲取下載的文件名
  • +(NSURLConnection*) connectionWithRequest:delegate:
    During the download the connection maintains a strong reference to the delegate. It releases that strong reference when the connection finishes loading, fails, or is canceled. connection對代理方法強引用

問題2的解決辦法:
保存文件的思路:
a.拼接完成寫入磁盤
b.下載一個寫一個
1>NSFileHandle
2>NSOutputStream

** a.拼接完成寫入磁盤**
1.生成目標(biāo)文件路徑
2.在didReceiveData里拼接數(shù)據(jù)
3.拼接完成寫入磁盤(在完成下載的方法里)
4.釋放內(nèi)存

//文件保存的路徑
@property (nonatomic , strong) NSString *targetFilePath;
//用來每次接收到數(shù)據(jù)戳吝,拼接數(shù)據(jù)使用
@property (nonatomic , strong) NSMutableData *fileData;
//代理
//1.接收到服務(wù)器的響應(yīng) :狀態(tài)行和響應(yīng)頭,用來做一些準(zhǔn)備工作
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"%@",response);
    
    //放到沙盒
    //NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    //self.targetFilePath = [cache stringByAppendingPathComponent:response.suggestedFilename];
    //生成目標(biāo)文件路徑    
    self.targetFilePath = [@"/Users/apple/xxxxxx"stringByAppendingPathComponent:response.suggestedFilename];//下載后的文件名字不變
    //刪除文件浩销,如果文件存在,就會直接刪除听哭,如果文件不存在就什么也不做
    [[NSFileManager defaultManager]removeItemAtPath:self.targetFilePath error:NULL];
}

- (NSMutableData *)fileData{
    if (_fileData == nil) {
        _fileData = [[NSMutableData alloc]init];
    }
    return _fileData;
}

//2.接收到服務(wù)器的數(shù)據(jù) :次方法可能會執(zhí)行多次慢洋,因為會接收到多個data
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
    NSLog(@"接收到的數(shù)據(jù)長度%tu",data.length);
    
    //拼接數(shù)據(jù)
//     a.拼接完成寫入磁盤
    [self.fileData appendData:data];
}

//3.所有數(shù)據(jù)加載完成 : 所有數(shù)據(jù)傳輸完畢之后被調(diào)用,是最后一個的通知
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
     //a.拼接完成寫入磁盤
    [self.fileData writeToFile:self.targetFilePath atomically:YES];
    //釋放內(nèi)存
    self.fileData = nil;//不釋放的話陆盘,內(nèi)存一直被占用普筹,文件多大被占用的就有多大
}
  • 把下載好的文件放到沙盒:
NSString *cache = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
NSString *file = [cache stringByAppendingPathComponent:response.suggestedFilename];```
response.suggestedFilename:建議保存的文件名
- 文件寫入磁盤后要釋放data:不釋放的話,內(nèi)存一直被占用隘马,文件多大被占用的就有多大

存在的問題:測試結(jié)果:也是存在內(nèi)存峰值

** b.下載一個寫一個**
1>NSFileHandle

//2.接收到服務(wù)器的數(shù)據(jù) :次方法可能會執(zhí)行多次太防,因為會接收到多個data

  • (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

    //拼接數(shù)據(jù)
    // b.下載一個寫一個
    [self writeToFileWithData:data];
    }

  • (void)writeToFileWithData:(NSData )data{
    //文件操作
    /

    NSFileManager:主要功能,創(chuàng)建目錄酸员,檢查目錄是否存在蜒车,遍歷目錄,刪除文件...針對文件的操作幔嗦,類似于Finder
    NSFileHandle:文件“句柄(文件指針)”酿愧,如果在開發(fā)中,看到Handle這個單詞崭添,就意味著是對前面的單詞“File”進行操作的對象
    主要功能寓娩,就是對同一個文件進行二進制的讀寫操作的對象。
    */

    //如果文件不存在呼渣,fp在實例化的結(jié)果是空
    NSFileHandle *fp = [NSFileHandle fileHandleForWritingAtPath:self.targetFilePath];
    //判斷文件是否存在
    //如果不存在,直接將數(shù)據(jù)存入磁盤
    if(fp == nil){
    [data writeToFile:self.targetFilePath atomically:YES];
    }//如果存在寞埠,將Data追繳到現(xiàn)有文件
    else{
    //1.將文件指針移到文件的末尾
    [fp seekToEndOfFile];
    //2.寫入文件
    [fp writeData:data];
    //3.關(guān)閉文件冒萄。在c語言開發(fā)中新思,凡是涉及到文件讀寫,打開和關(guān)閉通常是成對出現(xiàn)的
    [fp closeFile];
    }
    }

上面的寫法多次打開、關(guān)閉文件,下面進行改進:
  • (void)connection:(NSURLConnection )connection didReceiveResponse:(NSURLResponse )response
    {
    NSString
    ceches = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
    NSString
    filepath = [ceches stringByAppendingPathComponent:response.suggestedFilename];

    // 創(chuàng)建一個空的文件到沙盒中
    NSFileManager* mgr = [NSFileManager defaultManager];
    [mgr createFileAtPath:filepath contents:nil attributes:nil];
    // 創(chuàng)建一個用來寫數(shù)據(jù)的文件句柄對象
    self.writeHandle = [NSFileHandle fileHandleForWritingAtPath:filepath];
    }

  • (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
    {
    [self.writeHandle seekToEndOfFile];
    [self.writeHandle writeData:data];
    }

  • (void)connectionDidFinishLoading:(NSURLConnection *)connection
    {
    [self.writeHandle closeFile];
    self.writeHandle = nil;
    }

測試結(jié)果:徹底解決了內(nèi)存峰值的問題望拖。是傳統(tǒng)的文件操作方式。

2>NSOutputStream輸出流
1.創(chuàng)建流
2.打開流
3.將數(shù)據(jù)追加到流
4.關(guān)閉流

//保存文件的輸出流
/*

  • (void)open;寫入之前打開流
  • (void)close;完成之后關(guān)閉流
    */
    @property (nonatomic , strong) NSOutputStream *fileStream;

//代理
//1.接收到服務(wù)器的響應(yīng) :狀態(tài)行和響應(yīng)頭,用來做一些準(zhǔn)備工作

  • (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
    NSLog(@"%@",response);

    //生成目標(biāo)文件路徑
    self.targetFilePath = [@"/Users/apple/xxxxxx"stringByAppendingPathComponent:response.suggestedFilename];
    //刪除文件,如果文件存在揪阶,就會直接刪除,如果文件不存在沒就什么也不做
    [[NSFileManager defaultManager]removeItemAtPath:self.targetFilePath error:NULL];

    self.fileStream = [[NSOutputStream alloc]initToFileAtPath:self.targetFilePath append:YES];
    [self.fileStream open];
    }

//2.接收到服務(wù)器的數(shù)據(jù) :次方法可能會執(zhí)行多次患朱,因為會接收到多個data

  • (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{

    //拼接數(shù)據(jù)
    //將數(shù)據(jù)追加到文件流中
    [self.fileStream write:data.bytes maxLength:data.length];
    }

//3.所有數(shù)據(jù)加載完成 : 所有數(shù)據(jù)傳輸完畢之后被調(diào)用鲁僚,是最后一個的通知

  • (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    //關(guān)閉文件流
    [self.fileStream close];
    }

###斷點續(xù)傳
要實現(xiàn)斷點續(xù)傳要利用HTTP的range請求頭。bytes = 500-999表示從500-999的500個字節(jié)裁厅。續(xù)傳的Demo:[斷點續(xù)傳](http://www.cnblogs.com/GeekStar/p/4409714.html)
demo里面的例子只適合于應(yīng)用運行期間續(xù)傳冰沙。比如,一旦應(yīng)用在下載期間中途退出执虹,再次運行時拓挥,下載將會重新開始

// 設(shè)置請求頭數(shù)據(jù)
NSString *range = [NSString stringWithFormat:@"bytes=%lld-",self.currentLen];
[request setValue:range forHTTPHeaderField:@"Range"];

多線程斷點續(xù)傳:http://www.cnblogs.com/wendingding/p/3947550.html

###總結(jié)
- 小文件下載:可以使用sendAsynchronousRequest:queue:completionHandler 這個方法一次性返回整個下載到的文件,返回的data在內(nèi)存中袋励,如果下載一個幾百兆的東西侥啤,這樣會造成內(nèi)存峰值。( [NSData dataWithContentsOfURL:url]這個方法也是一樣)
- 大文件下載:使用代理


####NSURLConnection+NSRunLoop
新問題:下載默認(rèn)下在主線程工作茬故。下載本身是不是異步的(NSURLConnection實例運行在主線程)
 ```[conn setDelegateQueue:[[NSOperationQueue alloc]init]];```
指定了代理的工作隊列之后盖灸,整個下載仍然會受主線程的干擾,以及更新ui(進度條)不及時均牢。[在storyboard上添加了一個進度條以及一個uitextview糠雨,下載的時候,進度條會卡頓徘跪,滑動textview下載會暫停甘邀,停止滑動后又繼續(xù)下載]
 
``` NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];```在這里,delegate參數(shù)在api里面的說明如下:
 The delegate object for the connection. The connection calls methods on this delegate as the load progresses. Delegate methods are called on the same thread that called this method. For the connection to work correctly, the calling thread’s run loop must be operating in the default run loop mode.代理方法被調(diào)用connectionWithRequest:delegate:這個方法的同一個線程調(diào)用垮庐,為了保證連接的工作正常松邪,調(diào)用線程的runloop必須運行在默認(rèn)的運行循環(huán)模式下。

[一個異步網(wǎng)絡(luò)請求的坑:關(guān)于NSURLConnection和NSRunLoopCommonModes](http://www.hrchen.com/2013/06/nsurlconnection-with-nsrunloopcommonmodes/)這篇博文有提到這個問題:
如果是直接調(diào)用initWithRequest:delegate:startImmediately:(第三個參數(shù)用YES)或者方法initWithRequest:delegate:時(調(diào)用完connection就直接運行了)哨查,NSURLConnection會默認(rèn)運行在**NSDefaultRunLoopMode**模式下逗抑,即使再使用scheduleInRunLoop:forMode:設(shè)置運行模式也沒有用。如果NSURLConnection是運行在NSDefaultRunLoopMode寒亥,而當(dāng)前線程是**主線程**(NSURLConnection實例運行在主線程邮府,主線程有一個運行的runloop實例來支持NSURLConnection的異步執(zhí)行),并且UI上有類似滾動這樣的操作溉奕,那么主線程的Run Loop會運行在**UITrackingRunLoopMode**下褂傀,就無法響應(yīng)NSURLConnnection的回調(diào)。此時需要首先使用initWithRequest:delegate:startImmediately:(第三個參數(shù)為NO)生成NSURLConnection加勤,再重新設(shè)置NSURLConnection的運行模式為**NSRunLoopCommonModes**仙辟,那么UI操作和回調(diào)的執(zhí)行都將是非阻塞的同波,因為NSRunLoopCommonModes是一組run loop mode的集合,默認(rèn)情況下包含了NSDefaultRunLoopMode和UITrackingRunLoopMode叠国。
  • (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode在api中的說明
    將connection實例回調(diào)加入到一個runloop未檩,NSURLConnectionDelegate回調(diào)會在這個runloop中響應(yīng)
    Determines the run loop and mode that the connection uses to call methods on its delegate.
    By default, a connection is scheduled on the current thread in the default mode when it is created. If you create a connection with the initWithRequest:delegate:startImmediately: method and provide NO for the startImmediately parameter, you can schedule the connection on a different run loop or mode before starting it with the start method. You can schedule a connection on multiple run loops and modes, or on the same run loop in multiple modes.
    You cannot reschedule a connection after it has started.It is an error to schedule delegate method calls with both this method and the setDelegateQueue: method.
    方法參數(shù):
    aRunLoop:The NSRunLoop instance to use when calling delegate methods。
    mode:The mode in which to call delegate methods.
    這個方法不能和setDelegateQueue方法一起使用

總結(jié)一下個人理解粟焊,默認(rèn)情況下冤狡,代理方法被調(diào)用connectionWithRequest:delegate:這個方法的同一個線程調(diào)用,NSURLConnection默認(rèn)運行在NSDefaultRunLoopMode模式下吆玖;但是使用scheduleInRunLoop: forMode:可以設(shè)置代理方法運行在哪個runloop(相當(dāng)于是設(shè)置運行線程筒溃?)和mode下

//運行在主線程下
NSMutableURLRequest* request = [[NSMutableURLRequest alloc]
initWithURL:self.URL
cachePolicy:NSURLCacheStorageNotAllowed
timeoutInterval:self.timeoutInterval];
self.connection =[[NSURLConnection alloc] initWithRequest:request
delegate:self
startImmediately:NO];
//設(shè)置回調(diào)代理方法運行在那個runloop和mode,這里設(shè)置在當(dāng)前runloop也就是主線程里面執(zhí)行代理方法
[self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
[self.connection start];


**1.為了不影響ui線程沾乘,把下載工作放到子線程里面**

dispatch_async(dispatch_get_global_queue(0, 0), ^{//這樣寫怜奖,下載不執(zhí)行了。[conn start];之后子線程被回收釋放內(nèi)存空間
//1.url
NSString *urlstr = @"xxxx";
urlstr = [urlstr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlstr];

    //2.request
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.connection翅阵,不做設(shè)置的話是在主線程中執(zhí)行之后的下載 
    NSLog(@"start,%@",[NSThread currentThread]);
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
    
    //設(shè)置代理工作的操作隊列
    [conn setDelegateQueue:[[NSOperationQueue alloc]init]];
    
    //4.啟動連接
    [conn start];

});

把網(wǎng)絡(luò)操作方法放到子線程執(zhí)行以后歪玲,回調(diào)代理方法無法執(zhí)行,無法下載掷匠。
原因:主線程runloop會自動啟動滥崩,子線程runloop默認(rèn)不啟動。將網(wǎng)絡(luò)操作放在異步執(zhí)行讹语,異步的runloop不啟動钙皮,沒有辦法監(jiān)聽到網(wǎng)絡(luò)事件。[conn start];之后子線程被回收釋放內(nèi)存空間顽决。

[一個異步網(wǎng)絡(luò)請求的坑:關(guān)于NSURLConnection和NSRunLoopCommonModes](http://www.hrchen.com/2013/06/nsurlconnection-with-nsrunloopcommonmodes/)這篇博文有提到這個問題:如果是用GCD在其他線程中啟動NSURLConnection:不會得到NSURLConnection回調(diào)短条,而從主線程中啟動NSURLConnection可以得到回調(diào),這是由于在GCD全局隊列中執(zhí)行時沒有運行Run Loop才菠,那么NSURLConnection也就無法觸發(fā)回調(diào)了茸时。
解決的辦法如下:

**2.啟動子線程runloop**
a.[[NSRunLoop currentRunLoop] run];使用這種方法使用,runloop永遠釋放不掉
b.開啟一個循環(huán)赋访,判斷下載是否完成可都。這種方法對系統(tǒng)消耗非常大

@property (nonatomic , assign , getter = isFinished) BOOL finished;

//主線程runloop會自動啟動,子線程runloop默認(rèn)不啟動
//將網(wǎng)絡(luò)操作放在異步執(zhí)行蚓耽,異步的runloop不啟動渠牲,沒有辦法監(jiān)聽到網(wǎng)絡(luò)事件
dispatch_async(dispatch_get_global_queue(0, 0), ^{//這樣寫,下載不執(zhí)行了步悠。[conn start];之后子線程被回收釋放內(nèi)存空間
    ......
    
    //4.啟動連接
    [conn start];
    
    self.finished = NO;
    //5.啟動運行循環(huán)

// a. [[NSRunLoop currentRunLoop] run];//這樣寫永遠釋放不掉
// b.
while (!self.isFinished) {
//啟動一個死循環(huán)嘱兼,每次監(jiān)聽0.1秒.
//對系統(tǒng)消耗非常大
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
}
});

  • (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    ......
    self.finished = YES;
    }

c.方法b的改進,例子里面使用到CFRunLoop

@property (nonatomic , assign) CFRunLoopRef rl;

//主線程runloop會自動啟動贤徒,子線程runloop默認(rèn)不啟動
//將網(wǎng)絡(luò)操作放在異步執(zhí)行芹壕,異步的runloop不啟動,沒有辦法監(jiān)聽到網(wǎng)絡(luò)事件
dispatch_async(dispatch_get_global_queue(0, 0), ^{//這樣寫接奈,下載不執(zhí)行了踢涌。[conn start];之后子線程被回收釋放內(nèi)存空間
    
    //1.url
    NSString *urlstr = @"xxxx";
    urlstr = [urlstr stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    NSURL *url = [NSURL URLWithString:urlstr];
    
    //2.request
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    
    //3.connection,不做設(shè)置的話是在主線程中執(zhí)行之后的下載
    //開始時的線程是由dispatch_async 創(chuàng)建的
    NSLog(@"start,%@",[NSThread currentThread]);
    NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
    
    //設(shè)置代理工作的操作隊列
    [conn setDelegateQueue:[[NSOperationQueue alloc]init]];
    //指定調(diào)度代理工作的操作隊列序宦。操作隊列的特點:添加任務(wù)睁壁,立即異步執(zhí)行,具體的線程程序員不能決定
    
    //4.啟動連接
    [conn start];
    
    //5互捌。啟動運行循環(huán)
    //CF框架

// CFRunLoopStop(CFRunLoopRef rl);停止指定的runloop
// CFRunLoopGetCurrent();當(dāng)前線程的runloop
// CFRunLoopRun();直接運行當(dāng)前線程的runloop
//1.拿到當(dāng)前的runloop
self.rl = CFRunLoopGetCurrent();
//2.啟動運行循環(huán)
CFRunLoopRun();
});

  • (void)connectionDidFinishLoading:(NSURLConnection *)connection{
    //結(jié)束時代理工作的線程潘明,是由指定的NSOperationQueue創(chuàng)建的,和創(chuàng)建下載操作的線程是不一樣的秕噪。
    //關(guān)閉runloop钳降,要關(guān)閉指定線程上的runloop。在這里拿到創(chuàng)建下載那個線程的runloop
    NSLog(@"finish,%@",[NSThread currentThread]);
    //關(guān)閉文件流
    ......
    CFRunLoopStop(self.rl);
    }
![Runloop的啟動和關(guān)閉示意圖.png](http://upload-images.jianshu.io/upload_images/1727123-40091d2fffa8ac38.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 開始時的下載線程是由dispatch_async 創(chuàng)建的,```NSLog(@"start,%@",[NSThread currentThread]);```
 結(jié)束時**代理方法**工作的線程腌巾,是由指定的NSOperationQueue創(chuàng)建的:```[conn setDelegateQueue:[[NSOperationQueue alloc]init]];```遂填,和創(chuàng)建下載操作的線程是不一樣的。```NSLog(@"finish,%@",[NSThread currentThread]);```這里打印的兩個線程結(jié)果是不一樣的澈蝙。
- ```[conn setDelegateQueue:[[NSOperationQueue alloc]init]];```指定調(diào)度**代理方法**工作的操作隊列吓坚。操作隊列的特點:添加任務(wù),立即異步執(zhí)行灯荧,具體的線程程序員不能決定.
- 啟動runloop:1.拿到當(dāng)前的runloop ```self.rl = CFRunLoopGetCurrent();```2.啟動runloop``` CFRunLoopRun();```
 關(guān)閉runloop:要關(guān)閉指定線程上的runloop礁击。在這里拿到創(chuàng)建下載那個線程的runloop ```CFRunLoopStop(self.rl);```

相關(guān)文章:https://blog.cnbluebox.com/blog/2014/07/01/cocoashen-ru-xue-xi-nsoperationqueuehe-nsoperationyuan-li-he-shi-yong/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逗载,隨后出現(xiàn)的幾起案子哆窿,更是在濱河造成了極大的恐慌,老刑警劉巖撕贞,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件更耻,死亡現(xiàn)場離奇詭異,居然都是意外死亡捏膨,警方通過查閱死者的電腦和手機秧均,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來号涯,“玉大人目胡,你說我怎么就攤上這事×纯欤” “怎么了誉己?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長域蜗。 經(jīng)常有香客問我巨双,道長噪猾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任筑累,我火速辦了婚禮袱蜡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慢宗。我一直安慰自己坪蚁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布镜沽。 她就那樣靜靜地躺著敏晤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缅茉。 梳的紋絲不亂的頭發(fā)上嘴脾,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音宾舅,去河邊找鬼统阿。 笑死,一個胖子當(dāng)著我的面吹牛筹我,可吹牛的內(nèi)容都是我干的扶平。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔬蕊,長吁一口氣:“原來是場噩夢啊……” “哼结澄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起岸夯,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤麻献,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猜扮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體勉吻,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年旅赢,在試婚紗的時候發(fā)現(xiàn)自己被綠了齿桃。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡煮盼,死狀恐怖短纵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情僵控,我是刑警寧澤香到,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響悠就,放射性物質(zhì)發(fā)生泄漏千绪。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一理卑、第九天 我趴在偏房一處隱蔽的房頂上張望翘紊。 院中可真熱鬧,春花似錦藐唠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至自赔,卻和暖如春妈嘹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绍妨。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工润脸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人他去。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓毙驯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親灾测。 傳聞我的和親對象是個殘疾皇子爆价,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

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

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,139評論 30 470
  • 父類實現(xiàn)深拷貝時,子類如何實現(xiàn)深度拷貝媳搪。父類沒有實現(xiàn)深拷貝時铭段,子類如何實現(xiàn)深度拷貝。? 深拷貝同淺拷貝的區(qū)別:淺拷...
    JonesCxy閱讀 1,000評論 1 7
  • 1秦爆,NSObject中description屬性的意義序愚,它可以重寫嗎?答案:每當(dāng) NSLog(@"")函數(shù)中出現(xiàn) ...
    eightzg閱讀 4,143評論 2 19
  • 1.請簡單說明多線程技術(shù)的優(yōu)點和缺點? 優(yōu)點能適當(dāng)提高程序的執(zhí)行效率能適當(dāng)提高資源的利用率(CPU/內(nèi)存利用率) ...
    彼岸的黑色曼陀羅閱讀 457評論 0 2
  • 該文章屬于<簡書 — Timhbw>原創(chuàng)等限,轉(zhuǎn)載請注明: <簡書社區(qū) — Timhbw>http://www.jia...
    伯虔閱讀 17,101評論 3 158