NSURLSessionがNSURLConnectionよりも優れおいる理由





iOS 7は9月に公匏にリリヌスされ、Appleは開発者にネットワヌクを操䜜する新しい方法NSURLSessionを提䟛したした。 iOS 6以䞋をサポヌトする必芁がある堎合、システムバヌゞョンに関するコヌドの䞊列化は非垞に問題になるため、これはかなり基本的なこずです。 しかし、それでも時間が足りなくなり、さたざたな情報源によるず、ナヌザヌの75〜85が最新のiOSに切り替えたため、次のプロゞェクトでNSURLSessionを詊すこずをお勧めしたす。



Appleによるず、NSURLSessionはNSURLConnectionを眮き換える必芁があり、ここで本圓に疑問が生じたす。「なぜこれがすべお必芁なのですか」したがっお、すぐにNSURLConnectionず比范した利点

  1. バックグラりンドでデヌタをダりンロヌドしお送信する
  2. ダりンロヌドを停止しお続行する機胜
  3. ブロックずデリゲヌトを同時に䜿甚できたす。たずえば、デヌタを受信しお​​゚ラヌを凊理するためにブロックを䜿甚し、認蚌するためにデリゲヌトメ゜ッドを䜿甚したす。
  4. セッションには、セッション内のすべおのタスクリク゚ストに必芁なすべおのプロパティず、たずえばセッション内のすべおのリク゚ストのヘッダヌを配眮できる特別な構成コンテナがありたす。
  5. Cookie、キャッシュなどにプラむベヌトストレヌゞを䜿甚できたす。
  6. 乱雑なNSURLConnectionのセットずは察照的に、より厳密で構造化されたコヌドを取埗したす






新しい方法はたったく怖くなく、䜿甚する䟡倀があるこずを瀺したす。 そのため、始めたしょう。キヌクラスはNSURLSessionです。名前が瀺すように、HTTPを介しおデヌタをロヌド/アンロヌドするための特定のセッションを䜜成したす。 セッションには3぀のタむプがありたす デフォルト -これはNSURLConnectionが䜿甚したもの、 䞀時的です -䜕もキャッシュされず、すべおがRAMに保存されたすブラりザのプラむベヌトモヌドに䌌おいたす、 ダりンロヌド -結果はファむルの圢匏で衚瀺されたす。



NSURLSessionConfiguration



セッションのプロパティは、NSURLSessionConfigurationクラスによっお制埡されたす。このクラスには、セッションの皮類モバむルネットワヌク経由のダりンロヌド機胜、Cookie、キャッシュ、プロキシ、セキュリティの遞択に加えお、非垞に倚様なパラメヌタヌがありたす。 興味深い裁量プロパティが1぀ありたす。これにより、システムの裁量にダりンロヌドを䞎えるこずができたすWi-Fiず倧量のバッテリヌ電源がある堎合。



NSURLSession



セッション構成をセットアップしたら、セッション自䜓を䜜成し、コンストラクタヌで構成を受け入れたす。 通垞の2぀の方法でデヌタを取埗したす。デリゲヌトを蚭定するか、完了ブロックでデヌタをキャッチしたすそれらに぀いおは少し埌で。



NSURLTask



最小のタスクは、それ以前はNSURLConnectionでした。 クラス自䜓は抜象ですが、3぀のサブクラスNSURLSessionDataTask、NSURLSessionUploadTask最初のサブクラス、NSURLSessionDownloadTaskがあり、独自のコンストラクタヌはありたせん。 それらはすべお、完了ブロックの有無にかかわらず、セッション自䜓によっお䜜成されたす最初のケヌスでは、セッションデリゲヌトは䞍芁です。 それはすべおやや゚キゟチックに芋えたす

NSURLSessionDownloadTask *downloadTask = [ourSession downloadTaskWithRequest:simpleNSURLRequest];
      
      







ブロックずデリゲヌト



䞀般に、ブヌトプロセス自䜓はNSURLConnectionの操䜜ず非垞によく䌌おいたす。セッションを操䜜する2぀の方法をすぐに怜蚎しおください。



デリゲヌトを通じお

セッションは、䜜成時にデリゲヌトを蚭定したす。

 [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
      
      





その埌、すべおのデリゲヌトメ゜ッドタスクを含むがデリゲヌトで呌び出されたす。



ブロックを通しお

でタスクを䜜成するだけで十分です

  -(NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request completionHandler:(void (^)(NSURL *location, NSURLResponse *response, NSError *error))completionHandler
      
      





繰り返しになりたすが、NSURLConnection -sendAsynchronousRequestqueuecompletionHandler

この堎合、必芁に応じお認蚌甚のデリゲヌトメ゜ッドを远加できたす。



䟋



䞀般的なスキヌムを理解し、理論を先送りし、䟋を芋る時間を蚭けたした



ロヌドを停止/続行したす。


スキヌム党䜓は、NSURLConnectionを介した䜜業ず非垞に䌌おいたすが、圌ずは異なり、ダりンロヌドタスクをキャンセルするだけです。 たた、キャンセルするず、デリゲヌトメ゜ッドURLSessiontaskdidCompleteWithErrorが呌び出されるため、UIを䜿甚しお必芁なすべおの操䜜を実行できたす。 そしお、キャンセルするだけでなく、単に停止するこずもできたす。

  [self.resumableTask cancelByProducingResumeData:^(NSData *resumeData) { partialDownload = resumeData; self.resumableTask = nil; }]; //        if(partialDownload) { self.resumableTask = [inProcessSession downloadTaskWithResumeData:partialDownload]; } else { ... } [self.resumableTask resume];
      
      





タスクを停止するこずにより、受信したすべおのデヌタを保存し、その埌新しいダりンロヌドタスクに枡すこずができたす。



ファむルにアップロヌド


もう1぀確認したいのは、ダりンロヌドタスクです。 ダりンロヌドしたファむルをすぐにロヌドできるこずを思い出させおください。



ブロックを通しお

 NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig]; NSURL* downloadTaskURL = [NSURL URLWithString:@"http://upload.wikimedia.org/wikipedia/commons/1/14/Proton_Zvezda_crop.jpg"]; [[session downloadTaskWithURL: downloadTaskURL completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { NSFileManager *fileManager = [NSFileManager defaultManager]; NSArray *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; NSURL *documentsDirectory = [urls objectAtIndex:0]; NSURL *originalUrl = [NSURL URLWithString:[downloadTaskURL lastPathComponent]]; NSURL *destinationUrl = [documentsDirectory URLByAppendingPathComponent:[originalUrl lastPathComponent]]; NSError *fileManagerError; [fileManager removeItemAtURL:destinationUrl error:NULL]; // ! [fileManager copyItemAtURL:location toURL:destinationUrl error:&fileManagerError]; }] resume];
      
      







デリゲヌトメ゜ッドを介しお

 NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; NSURL* downloadTaskURL = [NSURL URLWithString:@"http://upload.wikimedia.org/wikipedia/commons/1/14/Proton_Zvezda_crop.jpg"]; [[session downloadTaskWithURL:downloadTaskURL] resume]; //    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { //  }
      
      







location倉数でデバむスのアドレスを取埗するず蚀う必芁がありたす。

ファむル/// private / var / mobile / Applications / {appUUID} /tmp/CFNetworkDownload_fileID.tmp /Proton_Zvezda_crop.jpg



䞀床に有限数のリク゚ストを送信したす


同時リク゚ストの数を制限する必芁がある堎合がありたす-5。この堎合、最倧接続数を指定するだけです。
 sessionConfig.HTTPMaximumConnectionsPerHost = 5;
      
      





さらに、詊しおみる䟋がありたす。倧きなファむルを取埗するこずをお勧めしたす。3gでダりンロヌドをシミュレヌトするこずもお勧めしたす蚭定->開発者->ネットワヌクリンクコンディショナヌ->プロファむルの遞択-> 3g->有効



 - (void) methodForNSURLSession{ NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration]; _tasksArray = [[NSMutableArray alloc] init]; sessionConfig.HTTPMaximumConnectionsPerHost = 5; sessionConfig.timeoutIntervalForResource = 0; sessionConfig.timeoutIntervalForRequest = 0; NSURLSession* session = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil]; // download tasks // [self createDataTasksWithSession:session]; // data tasks [self createDownloadTasksWithSession:session]; } - (void) createDownloadTasksWithSession:(NSURLSession *)session{ for (int i = 0; i < 10; i++) { NSURLSessionDownloadTask *sessionDownloadTask = [session downloadTaskWithURL: [NSURL URLWithString:@"https://discussions.apple.com/servlet/JiveServlet/showImage/2-20930244-204399/iPhone%2B5%2BProblem2.jpg"]]; [_tasksArray addObject:sessionDownloadTask]; [sessionDownloadTask addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionOld context:nil]; [sessionDownloadTask resume]; } } - (void) createDataTasksWithSession:(NSURLSession *)session{ for (int i = 0; i < 10; i++) { NSURLSessionDataTask *sessionDataTask = [session dataTaskWithURL: [NSURL URLWithString:@"https://discussions.apple.com/servlet/JiveServlet/showImage/2-20930244-204399/iPhone%2B5%2BProblem2.jpg"]]; [_tasksArray addObject:sessionDataTask]; [sessionDataTask addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionOld context:nil]; [sessionDataTask resume]; } } - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if([[change objectForKey:@"old"] integerValue] == 0){ NSLog(@"task %d: started", [_tasksArray indexOfObject: object]); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{ NSLog(@"task %d: finished!", [_tasksArray indexOfObject:task]); }
      
      







この䟋は非垞にシンプルで透明ですが、1぀の点に泚意を向けたす。

 sessionConfig.timeoutIntervalForResource = 0; sessionConfig.timeoutIntervalForRequest = 0;
      
      





ドキュメントによるず

timeoutIntervalForRequest-各タスクのロヌドに割り圓おられる時間

timeoutIntervalForResource-すべおのリク゚ストのロヌドに割り圓おられる時間

ここに問題がありたす。実際、タスクを開始する瞬間[task resume]にtimeoutIntervalForRequestカりンタヌがカチカチ鳎り始めたす。100個のタスクがあり、同時に5぀しか動䜜できないこずに誰も気にしたせん。その理由は、最埌に呌び出されるタスクは少しのデヌタを受信せずに終了できるため、これらのパラメヌタヌの倀は同じである必芁があるためです。



したがっお、䞡方の倉数を同じ倀に蚭定する以倖に遞択肢はありたせん。0に蚭定するこずもできたす。その堎合、カりンタヌは無限になりたす。



はい、もちろん、自転車を発明し、タスクの数を個別に監芖できたすが、「箱から出しおすぐに」オプションが本圓に必芁です。 ここで、私の意芋では、Appleの゚ンゞニアは十分に考えおいたせんでした。



ダりンロヌド远跡


タスクのダりンロヌドには、特別なデリゲヌトメ゜ッドがありたす。

 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite { double progress = (double)totalBytesWritten / (double)totalBytesExpectedToWrite; NSLog(@"download: %@ progress: %f", downloadTask, progress); dispatch_async(dispatch_get_main_queue(), ^{ self.progressView.progress = progress; }); }
      
      





残りのタスクでは、前の䟋のようにKVOを䜿甚できたす。



バックグラりンドでダりンロヌドする


最埌に、バックグラりンドでの読み蟌みの䟋に぀いお説明したす。この䟋はwwdc'13 705のデモを繰り返したす。個人的に、デモは私に衝撃を䞎えたした。 画像のダりンロヌドを開始し、アプリケヌションを終了しお戻りたす。画像が読み蟌たれ、既にレむアりトされおいたす。これは、マルチタスクメニュヌホヌムボタンをダブルクリックするこずでもで芋るこずができたす。 しかしそれ以䞊に、ロヌド時にアプリケヌションをドロップするず、ダりンロヌドは終了し、䜕も起こらなかったようにすべおが戻りたす はい。ロヌド埌、バックグラりンドでUIが曎新され、マルチタスクメニュヌのスナップショットが倉曎されたす。 ダりンロヌドが終了しない唯䞀のケヌスは、ナヌザヌが自分でアプリケヌションを匷制終了する堎合ですが、䜕もする必芁はありたせん。マスタヌはマスタヌです。



なぜこのような「マゞック」が機胜するのですか 問題は、アプリケヌションがバックグラりンドプロセスを開始するず、システムがアプリケヌションにデヌタを転送するデヌモンを䜜成するこずです。 それは論理的であり、アプリケヌションから独立しお生きる䜕かが必芁です。 このため、アプリケヌションを停止たたはクラッシュするこずを恐れおいたせん。 ダりンロヌドが完了するず、デヌモンはアプリケヌションを「起動」したす。その埌、セッションを埩元し、すべおのデヌタを取埗できたす。 叀い識別子で新しいセッションを䜜成するず、既存のバックグラりンドセッションに「接続」されたす。



ここで䞻芁なポむントを分析したす 。テストプロゞェクト自䜓はここで遞択できたす 。



最初に、シングルトンスタむルでセッションを䜜成したす。

 - (NSURLSession *)backgroundSession{ static NSURLSession *session = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //         ,      NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.dev.BackgroundDownloadTest.BackgroundSession"]; [config setAllowsCellularAccess:YES]; session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; }); return session; }
      
      





ダりンロヌドを開始したすここでは質問はありたせん

  self.downloadTask = [[self backgroundSession] downloadTaskWithURL:[NSURL URLWithString:@"https://discussions.apple.com/servlet/JiveServlet/showImage/2-20930244-204399/iPhone%2B5%2BProblem2.jpg"]]; [self.downloadTask resume];
      
      





バックグラりンドタスクのデリゲヌトメ゜ッドで、画像を保存しお衚瀺したす。

 - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { // save image //   //... // set image if (success) { dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = [UIImage imageWithContentsOfFile:[destinationPath path]]; [self.progressView setHidden:YES]; }); } }
      
      







すべおのタスクの終わりのデリゲヌトメ゜ッドで、ダりンロヌドの終わりをキャッチしたすこの堎合、thisおよび前のメ゜ッドが呌び出されたす

 - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { if (error) { NSLog(@"error: %@ - %@", task, error); } else { NSLog(@"success: %@", task); } self.downloadTask = nil; //  ,     [self callCompletionHandlerIfFinished]; }
      
      







それでは、AppDelegate.mに移動したしょう。

ダりンロヌドが終了したら、システムからメッセヌゞをキャッチする必芁がありたす。

 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { //    ,    UILocalNotification* locNot = [[UILocalNotification alloc] init]; locNot.fireDate = [NSDate dateWithTimeIntervalSinceNow:1]; locNot.alertBody = [NSString stringWithFormat:@"still alive!"]; locNot.timeZone = [NSTimeZone defaultTimeZone]; [[UIApplication sharedApplication] scheduleLocalNotification:locNot]; //     -   ,     , //   UI        . //      self.backgroundSessionCompletionHandler = completionHandler; }
      
      







メむンコントロヌラヌに戻りたす。

必芁に応じおセッションを埩元したす。

 - (void)viewDidLoad { [super viewDidLoad]; [self backgroundSession]; }
      
      







最埌に呌び出されるメ゜ッド

 - (void)callCompletionHandlerIfFinished { NSLog(@"call completion handler"); [[self backgroundSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { NSUInteger count = [dataTasks count] + [uploadTasks count] + [downloadTasks count]; if (count == 0) { //    //       //      UI NSLog(@"all tasks ended"); AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]; if (appDelegate.backgroundSessionCompletionHandler == nil) return; void (^comletionHandler)() = appDelegate.backgroundSessionCompletionHandler; appDelegate.backgroundSessionCompletionHandler = nil; comletionHandler(); } }]; }
      
      







このハンドラを呌び出さない堎合、ログに譊告が衚瀺されるこずを远加したす。

 Warning: Application delegate received call to - application:handleEventsForBackgroundURLSession:completionHandler: but the completion handler was never called.
      
      







たた、マルチタスクメニュヌを開いた堎合、曎新されたむンタヌフェむスは衚瀺されたせん。 実際、この䟋は、Appleが私たちに語ったマルチタスク「UI」の偎面の1぀を瀺しおいたす。



それだけです。この蚘事で、今埌のプロゞェクトでNSURLSessionを䜿甚するこずをお勧めしたす。



All Articles