閱讀目錄
一、整體介紹
NSURLSession在2013年隨著iOS7的發(fā)布一起面世,蘋(píng)果對(duì)它的定位是作為NSURLConnection的替代者摆尝,然后逐步將NSURLConnection退出歷史舞臺(tái)《嗤В現(xiàn)在使用最廣泛的第三方網(wǎng)絡(luò)框架:AFNetworking逻恐、SDWebImage等等都使用了NSURLSession。作為iOS開(kāi)發(fā)人員峻黍,應(yīng)該緊隨蘋(píng)果的步伐复隆,不斷的學(xué)習(xí),無(wú)論是軟件的更新姆涩、系統(tǒng)的更新挽拂、API的更新,而不能墨守成規(guī)骨饿。
Session翻譯為中文意思是會(huì)話亏栈,我們知道,在七層網(wǎng)絡(luò)協(xié)議中有物理層->數(shù)據(jù)鏈路層->網(wǎng)絡(luò)層->傳輸層->會(huì)話層->表示層->應(yīng)用層宏赘,那我們可以將NSURLSession類(lèi)理解為會(huì)話層绒北,用于管理網(wǎng)絡(luò)接口的創(chuàng)建、維護(hù)察署、刪除等等工作闷游,我們要做的工作也只是會(huì)話層之后的層即可,底層的工作NSURLSession已經(jīng)幫我們封裝好了贴汪。
另外還有一些Session储藐,比如AVAudioSession用于音視頻訪問(wèn),WCSession用于WatchOS通訊嘶是,它們都是建立一個(gè)會(huì)話钙勃,并管理會(huì)話,封裝一些底層聂喇,方便我們使用辖源。舉一反三蔚携。
二、使用的一般步驟
其核心就是對(duì)網(wǎng)絡(luò)任務(wù)進(jìn)行封裝克饶,實(shí)現(xiàn)多線程酝蜒。比如將一個(gè)網(wǎng)絡(luò)請(qǐng)求交給NSURLSession,最后NSURLSession將訪問(wèn)結(jié)果通過(guò)block回調(diào)返回矾湃,期間自動(dòng)實(shí)現(xiàn)多線程亡脑,而且可以通過(guò)代理實(shí)現(xiàn)監(jiān)聽(tīng)(是否成功,當(dāng)前的進(jìn)度等等)邀跃; 大致分為3個(gè)步驟:
1NSURL:請(qǐng)求地址霉咨,定義一個(gè)網(wǎng)絡(luò)資源路徑:
NSURL *url = [NSURL URLWithString:@"協(xié)議://主機(jī)地址/路徑?參數(shù)&參數(shù)"];
解釋如下:
協(xié)議:不同的協(xié)議,代表著不同的資源查找方式拍屑、資源傳輸方式途戒,比如常用的http,ftp等
主機(jī)地址:存放資源的主機(jī)的IP地址(域名)
路徑:資源在主機(jī)中的具體位置
參數(shù):參數(shù)可有可無(wú)僵驰,也可以多個(gè)喷斋。如果帶參數(shù)的話,用“?”號(hào)后面接參數(shù)蒜茴,多個(gè)參數(shù)的話之間用&隔開(kāi)
2NSURLRequest:請(qǐng)求星爪,根據(jù)前面的NSURL建立一個(gè)請(qǐng)求:
NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
參數(shù)解釋如下:
url:資源路徑
cachePolicy:緩存策略(無(wú)論使用哪種緩存策略,都會(huì)在本地緩存數(shù)據(jù))粉私,類(lèi)型為美劇類(lèi)型顽腾,取值如下:
NSURLRequestUseProtocolCachePolicy = 0 //默認(rèn)的緩存策略,使用協(xié)議的緩存策略
NSURLRequestReloadIgnoringLocalCacheData = 1 //每次都從網(wǎng)絡(luò)加載
NSURLRequestReturnCacheDataElseLoad = 2 //返回緩存否則加載毡鉴,很少使用
NSURLRequestReturnCacheDataDontLoad = 3 //只返回緩存崔泵,沒(méi)有也不加載,很少使用
timeoutInterval:超時(shí)時(shí)長(zhǎng)猪瞬,默認(rèn)60s憎瘸,這里設(shè)置為30s
另外,還可以設(shè)置其它一些信息陈瘦,比如請(qǐng)求頭幌甘,請(qǐng)求體等等,如下:
注意痊项,下面的request應(yīng)為NSMutableURLRequest锅风,即可變類(lèi)型
//告訴服務(wù)器數(shù)據(jù)為json類(lèi)型[request setValue:@"application/json"forHTTPHeaderField:@"Content-Type"];//設(shè)置請(qǐng)求體(json類(lèi)型)NSData *jsonData = [NSJSONSerialization dataWithJSONObject:@{@"userid":@"123456"} options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody= jsonData;
3NSURLSession:創(chuàng)建NSURLSession發(fā)送請(qǐng)求
為了方便使用,蘋(píng)果提供了一個(gè)全局的NSURLSession單例鞍泉,如同NSURLConnection一樣皱埠。這樣做的缺陷就是不能監(jiān)控,如果想要監(jiān)控每一個(gè)請(qǐng)求咖驮,則必須通過(guò)代理來(lái)監(jiān)聽(tīng)边器,我們知道單例是一對(duì)多的雷客,而代理是一對(duì)一监徘,因此必須自己實(shí)例化單獨(dú)的Session任務(wù)對(duì)象(NSURLConnection則很難)雇毫,來(lái)實(shí)現(xiàn)單獨(dú)監(jiān)控桨啃。
系統(tǒng)一共提供了5種任務(wù)類(lèi),繼承關(guān)系如下圖所示砚嘴。其中NSURLSessionTask為抽象類(lèi)十酣,不能實(shí)現(xiàn)網(wǎng)絡(luò)訪問(wèn),NSURLSessionStreanTask(以流的方式進(jìn)行網(wǎng)絡(luò)訪問(wèn))使用的比較少.使用的多的是dataTask际长、downloadTask耸采、uploadTask,即圖中紅色框框圈的部分也颤,基本滿足了網(wǎng)絡(luò)訪問(wèn)的基本需求:獲取數(shù)據(jù)(通常是JSON洋幻、XML等)郁轻、文件上傳翅娶、文件下載。這三個(gè)類(lèi)都是NSURLSessionTask這個(gè)抽象類(lèi)的子類(lèi)好唯,相比直接使用NSURLConnection,NSURLSessionTask支持任務(wù)的暫停竭沫、取消和恢復(fù),并且默認(rèn)任務(wù)運(yùn)行在其他非主線程中骑篙。
根據(jù)圖中代理協(xié)議的名字不難發(fā)現(xiàn)蜕提,每一個(gè)任務(wù)類(lèi)都有相對(duì)應(yīng)的代理協(xié)議,只有NSURLSessionUploadTask沒(méi)有對(duì)應(yīng)的代理協(xié)議靶端,因?yàn)镹SURLSessionUploadTask繼承自NSURLSessionDataTask谎势,因此NSURLSessionDataDelegate即為NSURLSessionUploadTask對(duì)應(yīng)的代理協(xié)議。
三 舉例
1.NSURLSession請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)
下面以蘋(píng)果提供的全局NSURLSession單例為例杨名,代碼如下:
///向網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)- (void)NSURLSessionTest {//1.創(chuàng)建url//請(qǐng)求一個(gè)網(wǎng)頁(yè)NSString *urlString =@"http://www.cnblogs.com/mddblog/p/5215453.html";
//一些特殊字符編碼urlString =[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL*url =[NSURL URLWithString:urlString];//2.創(chuàng)建請(qǐng)求 并:設(shè)置緩存策略為每次都從網(wǎng)絡(luò)加載 超時(shí)時(shí)間30秒NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30];//3.采用蘋(píng)果提供的共享sessionNSURLSession *sharedSession =[NSURLSession sharedSession];//4.由系統(tǒng)直接返回一個(gè)dataTask任務(wù)NSURLSessionDataTask *dataTask = [sharedSession dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError *_Nullable error) {//網(wǎng)絡(luò)請(qǐng)求完成之后就會(huì)執(zhí)行脏榆,NSURLSession自動(dòng)實(shí)現(xiàn)多線程N(yùn)SLog(@"%@",[NSThread currentThread]);if(data && (error ==nil)) {//網(wǎng)絡(luò)訪問(wèn)成功NSLog(@"data=%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}else{//網(wǎng)絡(luò)訪問(wèn)失敗NSLog(@"error=%@",error);
}
}];//5.每一個(gè)任務(wù)默認(rèn)都是掛起的,需要調(diào)用 resume 方法[dataTask resume];
}
2.NSURLSession文件下載
///文件下載- (void)NSURLSessionDownloadTaskTest {//1.創(chuàng)建urlNSString *urlString = [NSString stringWithFormat:@"http://localhost/周杰倫 - 楓.mp3"];//一些特殊字符編碼urlString =[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL*url =[NSURL URLWithString:urlString];//2.創(chuàng)建請(qǐng)求NSURLRequest *request =[NSURLRequest requestWithURL:url];//3.創(chuàng)建會(huì)話台谍,采用蘋(píng)果提供全局的共享sessionNSURLSession *sharedSession =[NSURLSession sharedSession];//4.創(chuàng)建任務(wù)NSURLSessionDownloadTask *downloadTask = [sharedSession downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError *_Nullable error) {if(error ==nil) {//location:下載任務(wù)完成之后,文件存儲(chǔ)的位置须喂,這個(gè)路徑默認(rèn)是在tmp文件夾下!//只會(huì)臨時(shí)保存,因此需要將其另存NSLog(@"location:%@",location.path);//采用模擬器測(cè)試趁蕊,為了方便將其下載到Mac桌面//NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];NSString *filePath =@"/Users/userName/Desktop/周杰倫 - 楓.mp3";
NSError*fileError;
[[NSFileManager defaultManager] copyItemAtPath:location.path toPath:filePath error:&fileError];if(fileError ==nil) {
NSLog(@"file save success");
}else{
NSLog(@"file save error: %@",fileError);
}
}else{
NSLog(@"download error:%@",error);
}
}];//5.開(kāi)啟任務(wù)[downloadTask resume];
}
3.NSURLSession文件上傳
3.1 采用uploadTask任務(wù)坞生,以數(shù)據(jù)流的方式進(jìn)行上傳
這種方式好處就是大小不受限制,上傳需要服務(wù)器端腳本支持掷伙,腳本源代碼見(jiàn)本文檔最后的附錄是己,客戶端示例代碼如下:
///以流的方式上傳,大小理論上不受限制任柜,但應(yīng)注意時(shí)間- (void) NSURLSessionBinaryUploadTaskTest {//1.創(chuàng)建url? 采用Apache本地服務(wù)器NSString *urlString =@"http://localhost/upload.php";urlString = [urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];NSURL *url =[NSURL URLWithString:urlString];//2.創(chuàng)建請(qǐng)求NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url];//文件上傳使用postrequest.HTTPMethod =@"POST";//3.開(kāi)始上傳? request的body data將被忽略卒废,而由fromData提供[[[NSURLSession sharedSession] uploadTaskWithRequest:request fromData:[NSData dataWithContentsOfFile:@"/Users/userName/Desktop/IMG_0359.jpg"]? ? completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError *_Nullable error) {if(error ==nil) {
NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}else{
NSLog(@"upload error:%@",error);
}
}] resume];
}
3.2 采用dataTask任務(wù)寒波,拼接表單的方式進(jìn)行上傳
上傳的關(guān)鍵是請(qǐng)求體部分的表單拼接,獲取本地上傳文件的類(lèi)型(MIME Types)升熊,至于具體的網(wǎng)絡(luò)上傳則很簡(jiǎn)單俄烁。 另外拼接表單的方式會(huì)有大小限制,即HTML的MAX_FILE_SIZE限制(可以自己設(shè)定级野,一般2MB)页屠。
根據(jù)上面的繼承關(guān)系圖,我們知道uploadTask是dataTask的子類(lèi)蓖柔,也可以使用uploadTask來(lái)代替dataTask辰企。在代碼示例中4.2步驟完全可以替換4.1步驟。這時(shí)况鸣,uploadTaskWithRequest函數(shù)的fromData可有可無(wú)牢贸,文件已在request里面包含。
注意:然而在蘋(píng)果官方對(duì)uploadTaskWithRequest函數(shù)的介紹:request的body data in this request object are ignored镐捧,會(huì)被忽略潜索,而測(cè)試時(shí)發(fā)現(xiàn)沒(méi)有被忽略,且request必須包含HTTPBody懂酱,反而fromData被忽略竹习。那么暫時(shí)理解為蘋(píng)果對(duì)uploadTaskWithRequest函數(shù)的使用時(shí)沒(méi)有考慮拼接表單的方式,那么當(dāng)我們使用拼接表單時(shí)列牺,建議不要使用uploadTask整陌,雖然這樣也能成功
服務(wù)器端用到的upload.php源代碼見(jiàn)本文最后的附錄
表單拼接格式如下,boundary作為分界線:
--boundary
Content-Disposition:form-data;name=”表單控件名稱”;filename=”上傳文件名稱”
Content-Type:要上傳文件MIME Types
要上傳文件二進(jìn)制數(shù)據(jù);--boundary--
拼接表單示例代碼:
///文件上傳- (void)NSURLSessionUploadTaskTest {//1.創(chuàng)建url? 采用Apache本地服務(wù)器NSString *urlString =@"http://localhost/upload/upload.php";
urlString=[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];
NSURL*url =[NSURL URLWithString:urlString];//2.創(chuàng)建請(qǐng)求NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url];//文件上傳使用postrequest.HTTPMethod =@"POST";
NSString*contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@",@"boundary"];
[request setValue:contentType forHTTPHeaderField:@"Content-Type"];//3.拼接表單瞎领,大小受MAX_FILE_SIZE限制(2MB)? FilePath:要上傳的本地文件路徑? formName:表單控件名稱泌辫,應(yīng)于服務(wù)器一致NSData* data = [self getHttpBodyWithFilePath:@"/Users/userName/Desktop/IMG_0359.jpg"formName:@"file"reName:@"newName.png"];
request.HTTPBody=data;//根據(jù)需要是否提供,非必須,如果不提供九默,session會(huì)自動(dòng)計(jì)算[request setValue:[NSString stringWithFormat:@"%lu",data.length] forHTTPHeaderField:@"Content-Length"];//4.1 使用dataTask[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError *_Nullable error) {if(error ==nil) {
NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}else{
NSLog(@"upload error:%@",error);
}
}] resume];#if0//4.2 開(kāi)始上傳 使用uploadTask? fromData:可有可無(wú)震放,會(huì)被忽略[[[NSURLSession sharedSession] uploadTaskWithRequest:request fromData:nil? ? completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError *_Nullable error) {if(error ==nil) {
NSLog(@"upload success:%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}else{
NSLog(@"upload error:%@",error);
}
}] resume];#endif}///filePath:要上傳的文件路徑? formName:表單控件名稱? reName:上傳后文件名- (NSData *)getHttpBodyWithFilePath:(NSString *)filePath formName:(NSString *)formName reName:(NSString *)reName
{
NSMutableData*data =[NSMutableData data];
NSURLResponse*response =[self getLocalFileResponse:filePath];//文件類(lèi)型:MIMEType? 文件的大小:expectedContentLength? 文件名字:suggestedFilenameNSString *fileType =response.MIMEType;//如果沒(méi)有傳入上傳后文件名稱,采用本地文件名!if(reName ==nil) {
reName=response.suggestedFilename;
}//表單拼接NSMutableString *headerStrM =[NSMutableStringstring];
[headerStrM appendFormat:@"--%@\r\n",@"boundary"];//name:表單控件名稱? filename:上傳文件名[headerStrM appendFormat:@"Content-Disposition: form-data; name=%@; filename=%@\r\n",formName,reName];
[headerStrM appendFormat:@"Content-Type: %@\r\n\r\n",fileType];
[data appendData:[headerStrM dataUsingEncoding:NSUTF8StringEncoding]];//文件內(nèi)容NSData *fileData =[NSData dataWithContentsOfFile:filePath];
[data appendData:fileData];
NSMutableString*footerStrM = [NSMutableString stringWithFormat:@"\r\n--%@--\r\n",@"boundary"];
[data appendData:[footerStrM? dataUsingEncoding:NSUTF8StringEncoding]];//NSLog(@"dataStr=%@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);returndata;
}///獲取響應(yīng)荤西,主要是文件類(lèi)型和文件名- (NSURLResponse *)getLocalFileResponse:(NSString *)urlString
{
urlString=[urlString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLFragmentAllowedCharacterSet]];//本地文件請(qǐng)求NSURL *url =[NSURL fileURLWithPath:urlString];
NSURLRequest*request =[NSURLRequest requestWithURL:url];
__block NSURLResponse*localResponse =nil;//使用信號(hào)量實(shí)現(xiàn)NSURLSession同步請(qǐng)求dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError *_Nullable error) {
localResponse=response;
dispatch_semaphore_signal(semaphore);
}] resume];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);returnlocalResponse;
}
四 NSURLSessionConfiguration
NSURLConnection是全局性的澜搅,即它的配置對(duì)全局有效,如果有兩個(gè)鏈接需要不同的cookies邪锌、證書(shū)這些公共資源勉躺,則NSURLConnection無(wú)法滿足要求,這時(shí)NSURLSession的優(yōu)勢(shì)則體現(xiàn)出來(lái)觅丰,NSURLSession可以同過(guò)NSURLSessionConfiguration可以設(shè)置全局的網(wǎng)絡(luò)訪問(wèn)屬性饵溅。
NSURLSessionConfiguration *config =[NSURLSessionConfiguration defaultSessionConfiguration];//delegateQueue:請(qǐng)求完成回調(diào)函數(shù)和代理函數(shù)的運(yùn)行線程,如果為nil則系統(tǒng)自動(dòng)創(chuàng)建一個(gè)串行隊(duì)列妇萄,不影響sessionTask的運(yùn)行線程N(yùn)SURLSession *session = [NSURLSession sessionWithConfiguration:configdelegate:self delegateQueue:[NSOperationQueue mainQueue]];
三種會(huì)話方式:
defaultSessionConfiguration:進(jìn)程內(nèi)會(huì)話(默認(rèn)會(huì)話)蜕企,類(lèi)似 NSURLConnection的標(biāo)準(zhǔn)配置咬荷,用硬盤(pán)來(lái)緩存數(shù)據(jù)。
ephemeralSessionConfiguration:臨時(shí)的進(jìn)程內(nèi)會(huì)話(內(nèi)存)轻掩,不會(huì)將cookie幸乒、緩存儲(chǔ)存到本地,只會(huì)放到內(nèi)存中唇牧,當(dāng)應(yīng)用程序退出后數(shù)據(jù)也會(huì)消失,可以用于實(shí)現(xiàn)“秘密瀏覽”
backgroundSessionConfiguration:建立后臺(tái)會(huì)話可以在應(yīng)用程序掛起罕扎,退出,崩潰的情況下運(yùn)行上傳和下載任務(wù)丐重,后臺(tái)另起一個(gè)線程腔召。另外,系統(tǒng)會(huì)根據(jù)設(shè)備的負(fù)載程度決定分配下載的資源扮惦,因此有可能會(huì)很慢甚至超時(shí)失敗臀蛛。
設(shè)置一些網(wǎng)絡(luò)屬性:
HTTPAdditionalHeaders:可以設(shè)置出站請(qǐng)求的數(shù)據(jù)頭
configuration.HTTPAdditionalHeaders =@{@"Accept":@"application/json",@"Accept-Language":@"en",@"Authorization": authString,@"User-Agent": userAgentString
};
networkServiceType,設(shè)置網(wǎng)絡(luò)服務(wù)類(lèi)型
NSURLNetworkServiceTypeDefault 默認(rèn)
NSURLNetworkServiceTypeVoIP VoIP
NSURLNetworkServiceTypeVideo 視頻
NSURLNetworkServiceTypeBackground 后臺(tái)
NSURLNetworkServiceTypeVoice 語(yǔ)音
allowsCellularAccess:允許蜂窩訪問(wèn)
timeoutIntervalForRequest:請(qǐng)求的超時(shí)時(shí)長(zhǎng)
requestCachePolicy:緩存策略
注意事項(xiàng):如果是自定義會(huì)話并指定了代理崖蜜,會(huì)話會(huì)對(duì)代理進(jìn)行強(qiáng)引用,在視圖控制器銷(xiāo)毀之前浊仆,需要取消網(wǎng)絡(luò)會(huì)話,否則會(huì)造成內(nèi)存泄漏
附錄——服務(wù)器端文件上傳PHP源代碼
以表單形式上傳纳猪,可以獲取文件名等等信息氧卧,注意images文件夾的權(quán)限應(yīng)為所有用戶可讀寫(xiě):
以文件流的形式上傳文件桃笙,文件的名字沒(méi)有動(dòng)態(tài)獲取氏堤,而是直接命名,注意images文件夾的權(quán)限應(yīng)為所有用戶可讀寫(xiě)
* $_POST 無(wú)法解釋二進(jìn)制流搏明,需要用到 $GLOBALS['HTTP_RAW_POST_DATA'] 或 php://input
* $GLOBALS['HTTP_RAW_POST_DATA'] 和 php://input 都不能用于 enctype=multipart/form-data
* @param? ? String? $file? 要生成的文件路徑
* @return? boolean*/functionbinary_to_file($file){$content=$GLOBALS['HTTP_RAW_POST_DATA'];//需要php.ini設(shè)置if(empty($content)){$content=file_get_contents('php://input');//不需要php.ini設(shè)置鼠锈,內(nèi)存壓力小}$ret=file_put_contents($file,$content,true);return$ret;
}$file_dir="images/image.png";//固定的文件名,注意設(shè)置images文件夾權(quán)限為所有用戶可讀寫(xiě)!P侵购笆!binary_to_file($file_dir);?>
示例代碼下載
github:https://github.com/mddios/NSURLSessionTest.git
關(guān)于NSURLSession還有很多,后續(xù)會(huì)再貼一些例子 未完待續(xù)虚循。同欠。。