URL加載系統(tǒng)之二:NSURLSession

NSURLSession及相關(guān)的類提供通過HTTP協(xié)議下載數(shù)據(jù)的API此改。該類提供了大量代理方法來支持認(rèn)證和后臺(tái)下載(程序未運(yùn)行或掛起時(shí))功能。

為了使用NSURLSession狼忱,我們的應(yīng)用會(huì)創(chuàng)建一系列的會(huì)話,每個(gè)會(huì)話負(fù)責(zé)協(xié)調(diào)一組相關(guān)數(shù)據(jù)的傳輸任務(wù)他巨。在每個(gè)會(huì)話中澡匪,我們的應(yīng)用添加一系列的任務(wù)唠摹,每個(gè)任務(wù)都表示一個(gè)對(duì)指定URL的請(qǐng)求测蹲。與大多數(shù)網(wǎng)絡(luò)API一樣喜喂,NSURLSession API是異步的勃刨。如果我們使用系統(tǒng)提供的代理饲帅,我們必須提供一個(gè)請(qǐng)求完成處理block侵蒙,以便在請(qǐng)求成功或失敗時(shí)返回?cái)?shù)據(jù)給我們的應(yīng)用也榄。如果我們提供自定義的代理對(duì)象惦费,則任務(wù)對(duì)象調(diào)用這些代理方法趟脂,并回傳從服務(wù)端獲取的數(shù)據(jù)(如果是文件下載泰讽,則當(dāng)傳輸完成時(shí)調(diào)用)。

NSURLSession提供了status和progress屬性昔期,并作為額外的信息傳遞給代理已卸。同時(shí)它支持取消、恢復(fù)硼一、掛起操作累澡,并支持?jǐn)帱c(diǎn)續(xù)傳功能。

要掌握NSURLSession的使用般贼,我們需要了解下URL會(huì)話的一些內(nèi)容

URL會(huì)話

在一個(gè)會(huì)話中的任務(wù)的行為取決于三個(gè)方面:

session的類型(由創(chuàng)建會(huì)話時(shí)的配置對(duì)象確定)

任務(wù)的類型

當(dāng)任務(wù)創(chuàng)建時(shí)應(yīng)用是否在前臺(tái)

NSURLSession支持以下三種會(huì)話類型:

默認(rèn)會(huì)話:行為與其它下載URL的Foundation方法類似愧哟。使用基于磁盤的緩存策略,并在用戶keychain中存儲(chǔ)證書哼蛆。

短暫會(huì)話(Ephemeral sessions):不存儲(chǔ)任何數(shù)據(jù)在磁盤中蕊梧;所有的緩存,證書存儲(chǔ)等都保存在RAM中并與會(huì)話綁定腮介。這樣肥矢,當(dāng)應(yīng)用結(jié)束會(huì)話時(shí),它們被自動(dòng)釋放叠洗。

后臺(tái)會(huì)話(Background sessions):類似于默認(rèn)會(huì)話橄抹,除了有一個(gè)獨(dú)立的進(jìn)程來處理所有的數(shù)據(jù)傳輸靴迫。

在一個(gè)會(huì)話中,NSURLSession支持三種任務(wù)類型

數(shù)據(jù)任務(wù):使用NSData對(duì)象來發(fā)送和接收數(shù)據(jù)楼誓。數(shù)據(jù)任務(wù)可以分片返回?cái)?shù)據(jù)玉锌,也可以通過完成處理器一次性返回?cái)?shù)據(jù)。由于數(shù)據(jù)任務(wù)不存儲(chǔ)數(shù)據(jù)到文件疟羹,所以不支持后臺(tái)會(huì)話.

下載任務(wù):以文件的形式接收數(shù)據(jù)主守,當(dāng)程序不運(yùn)行時(shí)支持后臺(tái)下載

上傳任務(wù):通常以文件的形式發(fā)送數(shù)據(jù),支持后臺(tái)上傳榄融。

NSURLSession支持在程序掛起時(shí)在后臺(tái)傳輸數(shù)據(jù)参淫。后臺(tái)傳輸只由使用后臺(tái)會(huì)話配置對(duì)象創(chuàng)建的會(huì)話提供。使用后臺(tái)會(huì)話時(shí)愧杯,由于實(shí)際傳輸是在一個(gè)獨(dú)立的進(jìn)程中傳輸涎才,且重啟應(yīng)用進(jìn)程相當(dāng)損耗資源,只有少量特性可以使用力九,所以有以下限制:

會(huì)話必須提供事件分發(fā)的代理耍铜。

只支持HTTP和HTTPS協(xié)議

只支持上傳和下載任務(wù)

總是伴隨著重定義操作

如果當(dāng)應(yīng)用在后臺(tái)時(shí)初始化的后臺(tái)傳輸,則配置對(duì)象的discretionary屬性為true

在iOS中跌前,當(dāng)我們的應(yīng)用不再運(yùn)行時(shí)棕兼,如果后臺(tái)下載任務(wù)完成或者需要證書,則系統(tǒng)會(huì)在后臺(tái)自動(dòng)重啟我們的應(yīng)用抵乓,同時(shí)調(diào)用UIApplicationDelegate對(duì)象的application:handlerEventsForBackgroundURLSession:completionHandler:方法伴挚。這個(gè)調(diào)用會(huì)提供啟動(dòng)的應(yīng)用的session的標(biāo)識(shí)。我們的應(yīng)用應(yīng)該存儲(chǔ)完成處理器灾炭,使用相同的標(biāo)識(shí)來創(chuàng)建后臺(tái)配置對(duì)象茎芋,然后使用配置對(duì)象來創(chuàng)建會(huì)話。新的會(huì)話會(huì)與運(yùn)行的后臺(tái)activity關(guān)聯(lián)蜈出。當(dāng)會(huì)話完成后臺(tái)下載任務(wù)時(shí)田弥,會(huì)給會(huì)話代理發(fā)送一個(gè)URLSessioinDidFinishEventsForBackgroundURLSession:消息。我們的代理對(duì)象然后調(diào)用存儲(chǔ)的完成處理器掏缎。

如果在程序掛起時(shí)有任何任務(wù)完成皱蹦,則會(huì)調(diào)用URLSession:downloadTask:didFinishDownloadingToURL:方法煤杀。同樣的眷蜈,如果任務(wù)需要證書,則NSURLSession對(duì)象會(huì)在適當(dāng)?shù)臅r(shí)候調(diào)用URLSession:task:didReceiveChallenge:completionHandler: 和URLSession:didReceiveChallenge:completionHandler:方法沈自。

這里需要注意的是必須為每個(gè)標(biāo)識(shí)創(chuàng)建一個(gè)會(huì)話酌儒,共享相同標(biāo)識(shí)的多個(gè)會(huì)話的行為是未定義的。

會(huì)話和任務(wù)對(duì)象實(shí)現(xiàn)了NSCopying協(xié)議:

當(dāng)應(yīng)用拷貝一個(gè)會(huì)話或任務(wù)對(duì)象時(shí)枯途,會(huì)獲取相同對(duì)象的指針

當(dāng)應(yīng)用拷貝一個(gè)配置對(duì)象時(shí)忌怎,會(huì)獲取一個(gè)可單獨(dú)修改的新的對(duì)象

創(chuàng)建并配置NSURLSession

我們下面舉個(gè)簡單的實(shí)例來說明一個(gè)NSURLSession與服務(wù)端的數(shù)據(jù)交互籍滴。

代碼清單1:聲明三種類型會(huì)話對(duì)象

@interface URLSession : NSObject

@property (nonatomic, strong) NSURLSession *backgroundSession;

@property (nonatomic, strong) NSURLSession *defaultSession;

@property (nonatomic, strong) NSURLSession *ephemeralSession;

@property (nonatomic, strong) NSMutableDictionary *completionHandlerDictionary;

- (void)addCompletionHandler:(CompletionHandlerType)handler forSession:(NSString *)identifier;

- (void)callCompletionHandlerForSession:(NSString *)identifier;

@end


NSURLSession提供了大量的配置選項(xiàng),包括:

支持緩存榴啸、cookie孽惰,認(rèn)證及協(xié)議的私有存儲(chǔ)

認(rèn)證

上傳下載文件

每個(gè)主機(jī)的配置最大數(shù)

超時(shí)時(shí)間

支持的TLS最小最小版本

自定義代理字典

控制cookie策略

控制HTTP管道行為

由于大部分設(shè)置都包含在一個(gè)獨(dú)立的配置對(duì)象中,所以我們可以重用這些配置鸥印。當(dāng)我們初始一個(gè)會(huì)話對(duì)象時(shí)勋功,我們指定了如下內(nèi)容

一個(gè)配置對(duì)象,用于管理其中的會(huì)話和任務(wù)的行為

一個(gè)代理對(duì)象库说,用于在收到數(shù)據(jù)時(shí)處理輸入數(shù)據(jù)狂鞋,及會(huì)話和任務(wù)中的其它事件,如服務(wù)端認(rèn)證潜的、確定一個(gè)資源加載請(qǐng)求是否應(yīng)該轉(zhuǎn)換成下載等骚揍。這個(gè)對(duì)象是可選的。但如果我們需要執(zhí)行后臺(tái)傳輸啰挪,則必須提供自定義代理信不。

在實(shí)例一個(gè)會(huì)話對(duì)象后,我們不能改變改變配置或代理脐供。

代碼清單2演示了如何創(chuàng)建一個(gè)會(huì)話

NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];

// 配置會(huì)話的緩存

NSString *cachePath = @"/MyCacheDirectory";

NSArray *pathList = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);

NSString *path = [pathList objectAtIndex:0];

NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];

NSString *fullCachePath = [[path stringByAppendingPathComponent:bundleIdentifier] stringByAppendingPathComponent:cachePath];

NSLog(@"Cache path: %@", fullCachePath);

NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:16384 diskCapacity:268435456 diskPath:cachePath];

defaultConfigObject.URLCache = cache;

defaultConfigObject.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;

self.defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];

除了后臺(tái)配置對(duì)象外浑塞,我們可以重用會(huì)話的配置對(duì)象來創(chuàng)建新的會(huì)話,正如上面所講的政己,拷貝一個(gè)配置對(duì)象會(huì)生成一個(gè)新的獨(dú)立的配置對(duì)象酌壕。我們可以在任何時(shí)候安全的修改配置對(duì)象。當(dāng)創(chuàng)建一個(gè)會(huì)話時(shí)歇由,會(huì)話會(huì)對(duì)配置對(duì)象進(jìn)行深拷貝卵牍,所以修改只會(huì)影響到新的會(huì)話。代理清單3演示了創(chuàng)建一個(gè)新的會(huì)話沦泌,這個(gè)會(huì)話使用重用的配置對(duì)象糊昙。

代碼清單3:重用會(huì)話對(duì)象

self.ephemeralSession = [NSURLSession sessionWithConfiguration:ephemeralConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];

ephemeralConfigObject.allowsCellularAccess = YES;

// ...

NSURLSession *ephemeralSessionWifiOnly = [NSURLSession sessionWithConfiguration:ephemeralConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];

使用NSURLSession獲取數(shù)據(jù)基本就是兩步:

創(chuàng)建一個(gè)配置對(duì)象及基于這個(gè)對(duì)象的會(huì)話

定義一個(gè)請(qǐng)求完成處理器來處理獲取到的數(shù)據(jù)。

如果使用系統(tǒng)提供的代理谢谦,只需要代碼清單4這幾行代碼即可搞定

代碼清單4:使用系統(tǒng)提供代理

NSURLSession *delegateFreeSession = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:[NSOperationQueue mainQueue]];

[delegateFreeSession dataTaskWithRequest:@"http://www.sina.com"

completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

NSLog(@"Got response %@", response);

}];

只是系統(tǒng)提供的代理只提供有限的網(wǎng)絡(luò)行為释牺。如果應(yīng)用需要更多的處理,如自定義認(rèn)證或后臺(tái)下載等回挽,則需要使用自定義的代理没咙。使用自定義代理來獲取數(shù)據(jù)時(shí),代理必須實(shí)現(xiàn)以下方法:

URLSession:dataTask:didReceiveData: 從請(qǐng)求提供數(shù)據(jù)給我們的任務(wù)千劈,一次一個(gè)數(shù)據(jù)塊

URLSession:task:didCompleteWithError: 表示任務(wù)已經(jīng)接受了所有的數(shù)據(jù)祭刚。

如果我們?cè)赨RLSession:dataTask:didReceiveData:方法返回后使用數(shù)據(jù),則需要將數(shù)據(jù)存儲(chǔ)在某個(gè)地方。

代碼清單5:演示了一個(gè)數(shù)據(jù)訪問實(shí)例:

NSURL *url = [NSURL URLWithString:@"http://www.sina.com"];

NSURLSessionDataTask *dataTask = [self.defaultSession dataTaskWithURL:url];

[dataTask resume];

如果遠(yuǎn)程服務(wù)器返回的狀態(tài)表示需要一個(gè)認(rèn)證涡驮,且認(rèn)證需要連接級(jí)別的處理時(shí)暗甥,NSURLSession將調(diào)用認(rèn)證相關(guān)代理方法。這個(gè)具體我們后面文章將詳細(xì)討論捉捅。

處理iOS后臺(tái)Activity

在iOS中使用NSURLSession時(shí)撤防,當(dāng)一個(gè)下載完成時(shí),會(huì)自動(dòng)啟動(dòng)我們的應(yīng)用棒口。應(yīng)用的代理方法application:handleEventsForBackgroundURLSession:completionHandler: 負(fù)責(zé)創(chuàng)建一個(gè)合適的會(huì)話即碗,存儲(chǔ)請(qǐng)求完成處理器,并在會(huì)話調(diào)用會(huì)話代理的URLSessionDidFinishEventsForBackgroundURLSession: 方法時(shí)調(diào)用這個(gè)處理器陌凳。代碼清單6與代碼清單7演示了這個(gè)處理流程

代碼清單6:iOS后臺(tái)下載的會(huì)話代理方法

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session

{

NSLog(@"background url session %@", session);

if (session.configuration.identifier)

{

[self callCompletionHandlerForSession:session.configuration.identifier];

}

}

- (void)callCompletionHandlerForSession:(NSString *)identifier

{

CompletionHandlerType handler = [self.completionHandlerDictionary objectForKey:identifier];

if (handler) {

[self.completionHandlerDictionary removeObjectForKey:identifier];

handler();

}

}

代碼清單7:iOS后臺(tái)下載的App 代理方法

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler

{

NSURLSessionConfiguration *backgroundConfigObject = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];

URLSession *sessionDelegate = [[URLSession alloc] init];

NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundConfigObject

delegate:sessionDelegate

delegateQueue:[NSOperationQueue mainQueue]];

[sessionDelegate addCompletionHandler:completionHandler forSession:identifier];

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剥懒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子合敦,更是在濱河造成了極大的恐慌初橘,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件充岛,死亡現(xiàn)場離奇詭異保檐,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)崔梗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門夜只,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蒜魄,你說我怎么就攤上這事扔亥。” “怎么了谈为?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵旅挤,是天一觀的道長。 經(jīng)常有香客問我伞鲫,道長粘茄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任秕脓,我火速辦了婚禮柒瓣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吠架。我一直安慰自己芙贫,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布诵肛。 她就那樣靜靜地躺著屹培,像睡著了一般。 火紅的嫁衣襯著肌膚如雪怔檩。 梳的紋絲不亂的頭發(fā)上褪秀,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音薛训,去河邊找鬼媒吗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛乙埃,可吹牛的內(nèi)容都是我干的闸英。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼介袜,長吁一口氣:“原來是場噩夢啊……” “哼甫何!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起遇伞,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤辙喂,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鸠珠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巍耗,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年渐排,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了炬太。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驯耻,死狀恐怖亲族,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情可缚,我是刑警寧澤孽水,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站城看,受9級(jí)特大地震影響女气,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜测柠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一炼鞠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧轰胁,春花似錦谒主、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春观游,著一層夾襖步出監(jiān)牢的瞬間搂捧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工懂缕, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留允跑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓搪柑,卻偏偏與公主長得像聋丝,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子工碾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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