2014-03-05 1 views
1

iphone 응용 프로그램을 개발하기 시작했으며 NSURLSession에 대한 조언과 내 데이터 다운로드 및 구문 분석을 올바르게 관리하는 방법에 대해 알아야합니다.NSURLSession을 올바르게 관리하기위한 조언

방금 ​​nsurlsession에서 데이터 다운로드에 대한 버그를 수정했지만 이러한 비동기 요청을 이해하는 것이 얼마나 어려웠는지에 관해서는 내 솔루션이 실제로 좋지 않다고 생각합니다. 또한 다운로드 버그가 나타납니다. 다운로드의 2 가지 다른 솔루션으로, 내가 뭔가를 잊어 버린 것 같아요 ...

In, 내 프로젝트를 내가 다른 XML 파일을 (그리고 때로는 사진과 함께 일부 zip 파일) 내가 전에 구문 분석을 표시하는 데 필요한 다운로드 그들의 정보. 이러한 정보는 빠르게 변경 될 수 있으므로 페이지를 다시로드하면 다시 다운로드 할 수 있습니다. 나는 많은 코드를 다시 작성할 필요가없는 것처럼 같은 방식으로 모든 다운로드를 관리하는 쉬운 방법을 찾고있었습니다.

이 부분을 먼저 찾았습니다. project입니다. 그와

은, 난 그냥 다운로드를 관리하기 위해 해당 코드를 사용했다 :

NSString *downloadUrl = @"https://www.url.com"; 

     NSURL *location = [NSURL URLWithString:downloadUrl]; 
     // DownloadManager is my version of the CTSessionOperation of the github project 
     DownloadManager *operation = [DownloadManager new]; 
     operation.downloadUrl = downloadUrl; 
     operation.completionAction = ^(NSURL *xmlUrl, BOOL success){ 
      dispatch_async(dispatch_get_main_queue(), ^{ 
       if (success){ 
        regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]]; 
        [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:Nil waitUntilDone:YES]; 
       } 
      }); 
     }; 
     operation.isBackground = YES; 

     [operation enqueueOperation]; 

이 코드는 완벽을 다운로드 처음 작동합니다. 하지만 다운로드를 다시 시작하려고하면 다운로드가 안됩니다 (오류가 없으므로이 코드를 한 번 다운로드하면됩니다).

CTSessionOperation/DownloadManager으로 수정하여이 버그를 해결하십시오. 나는 그것을 만들기 위해 "dispatch_once"주석을 달았지만 좋은 해결책이라고는 생각하지 않는다. ...

나는 같은 버그를 야기한 다른 해결책을 시도했다.

 NSString *regionsUrl= @"url"; 

      NSURLSessionConfiguration *sessionConfig = 
      [NSURLSessionConfiguration defaultSessionConfiguration]; 
// My solution to the bug   
/*NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration 
                    backgroundSessionConfiguration:[NSString stringWithFormat:@"com.captech.mysupersession.BackgroundSession%d",numBGSession]]; */ 
      // numBGSession++; this is a static NSInteger 


NSURLSession *session = 
     [NSURLSession sessionWithConfiguration:backgroundConfiguration 
             delegate:teamChoiceDetailViewController 
           delegateQueue:nil]; 

     NSURLSessionDownloadTask *sessDLTask = 
     [session downloadTaskWithURL:[NSURL URLWithString:regionsUrl]]; 

     [sessDLTask resume]; 

및 위임에 : 나는이 코드 다운로드를 관리

-(void)URLSession:(NSURLSession *)session 
    downloadTask:(NSURLSessionDownloadTask *)downloadTask 
didFinishDownloadingToURL:(NSURL *)location 
{ 
    dispatch_async(dispatch_get_main_queue(), ^{ 
     self.regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]]; 
     [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:Nil waitUntilDone:YES]; 
    }); 
} 

이 솔루션, 나는 사용자 정의 NSURLSessionConfiguration을 다운로드하려고 할 때마다를 작성하여 버그를 피할 수 있습니다.

여기 있습니다. 나는이 2 가지 솔루션과 상당히 혼동한다. 나는 그들이 다운로드를 관리하는 적절한 방법인지 모르겠다. 나는 올바르게 버그를 수정했다고 생각하지 않는다. 그리고 나는 NSURLSession의 논리로 뭔가를 놓쳤음에 틀림 없다.

이 솔루션을 개선하기위한 조언이 있습니까? 아니면 다른 솔루션보다 훨씬 뛰어난 솔루션이 있습니까?

답변

0

관찰의 커플 : 당신이 배경 NSURLSession를 사용하는 경우

  1. , 이것은 표준 NSURLSession보다 느린 될 수 있음에 유의하십시오. 그리고 다운로드를 많이하는 경우 세션이 이전 요청 중 일부와 함께 백 로그 될 수 있으므로 다시 방문하면 아무 것도하지 않는 것처럼 보입니다 (사실, 존재하지 않거나 더 이상 보이지 않는 테이블 뷰 인스턴스를 업데이트하려고하는 이전 작업 요청). 이것은 왜 새로운 세션 생성이 제대로 작동 하는지를 설명 할 수 있습니다. 왜냐하면 새로운 세션이 이전에 대기열에 포함 된 작업 뒤에 새 작업을 대기 행렬에 넣지 않고 병렬로 실행하기 때문입니다.

    내가 제안 : (당신의 직관이 바로 여기 있기 때문에, 당신은 확실히이 실행 밖으로 이전 내용을 떠나, 세션의 무리를하고 싶지 않아)

    • dispatch_once 논리를 복원;

    • 이보기 컨트롤러로 돌아 가면 새 요청을 시작하기 전에 대기중인 요청을 취소하십시오. 당신은 뷰 컨트롤러를 유지하는

      • : 때문에 completionAction를 사용

    • 귀하의 초기 시도

      는 문제가있다. 그래서 대신 :

      operation.completionAction = ^(NSURL *xmlUrl, BOOL success){ 
          dispatch_async(dispatch_get_main_queue(), ^{ 
           if (success){ 
            regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]]; 
            [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:Nil waitUntilDone:YES]; 
           } 
          }); 
      }; 
      

      당신은 할 수 있습니다 : 당신은 또한 배경 NSURLSession와 함께이 completionAction을 사용하려는

      typeof(self) __weak weakSelf = self; 
      operation.completionAction = ^(NSURL *xmlUrl, BOOL success){ 
          dispatch_async(dispatch_get_main_queue(), ^{ 
           if (success){ 
            regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]]; 
            [weakSelf.tableView reloadData]; // don't need that `performSelectorOnMainThread` call 
           } 
          }); 
      }; 
      
    • 합니다. 다운로드가 백그라운드에서 완료되는 동안 앱이 종료되면이 완료 블록이 손실됩니다 (백그라운드 세션을 수행하는 목적을 상실 함).

      정말로 백그라운드 세션을 사용해야하는 경우이 completionAction 내부의 모든 논리를 다운로드 위임 방법 자체로 이동해야합니다. 이 completionAction을 백그라운드 세션과 함께 사용할 수 없으며 앱이 종료 될 때까지 기다릴 수 없습니다 (NSURLSessionDownloadTask 개체 일지라도). (A) 그 블록 이후 배경 NSURLSession (와 함께 완료 블록을 사용하여의인지 부조화가 있기 때문에 아래


, 나는이 CTSessionOperation 클래스에 대해 inveighed있는 내 원래의 대답이다 다운로드가 계속되는 경우에도 앱이 종료되면 손실됩니다.) (b) 비동기 작업을 위해 NSOperation을 비 동시성으로 사용하고 NSOperation 기반 구현의 주요 이점을 많이 상쇄합니다. 위의 두 가지 사항이 모두 문제가되는 것은 분명하지만, 이러한 우려는 위의 제 요점에 부수적이라고 생각합니다. 그러나 여기를 참고할 것입니다.

원래 답 :

당신은 무엇을 의미합니까 "완벽을 다운로드 처음 작동,하지만 난 다시 시작하려고 [하지] 경우"? 앱이 종료되고 백그라운드에서 계속 다운로드 중임을 말하는 것입니까? 원래 CTSessionOperation은 정확히 NSOperation 및 완료 블록의 표준 트릭을 사용하려고하기 때문에 제대로 처리하지 못했지만 그 모두는 앱이 종료 된 후에도 진행되는 NSURLSession 백그라운드 세션과 호환되지 않습니다. 응용 프로그램이 종료되면 NSURLSession 백그라운드 요청이 계속 진행되지만 모든 작업 및 완료 블록이 완전히 손실됩니다. 나는 이것이 CTSessionOperation가 여기에서 표를 놓쳤다라고 생각한다. 앱이 종료 된 후에도 진행되는 백그라운드 세션의 풍부함을 실제로 즐길 수는 없으며 백그라운드 다운로드 작업을 처음 시작할 때 생성 한 작업 및 완료 블록을 유지해야합니다.NSURLSessionDownloadTask 개체를 사용하고 대리자 메서드 만 사용해야합니다. 완료 블록이나 작업이 없습니다. CTSessionOperation의 구조적 결함 것으로 보인다 무엇을 내 비판에도 불구하고

dispatch_once을 주석하고 독특한 배경 세션을 만들거나 표준 전경 세션을 만드는 시도 구제는 확실히 하지 당신이 배경 다운로드를 필요로하는 경우 올바른 접근 방식이다. 아무튼 앱이 종료 된 후 CTSessionOperation이 백그라운드 세션을 처리하지 못하는 문제를 해결하려고하는 경우 실제로 잘못된 방향 인, methinks가 발생합니다.

그래서, 질문은 당신이 정말로 배경 NSURLSession의 모든 기능을 즐기고 싶은 여부 (이 경우 당신은 CTSessionOperation의 완료 블록 및 운영 패턴을 포기해야한다), 또는 당신은 비 배경 NSURLSession으로 확인 여부 앱이 종료 될 때 다운로드가 종료되고 사용자가 앱을 다시 실행할 때 처음부터 다시 시작하기를 원할뿐입니다.

+0

답장을 보내 주셔서 감사합니다. 완벽하게 작동합니다, 내가 예제를 의미, 만약 내가 tableViewController의 viewDidLoad 에서이 코드를 사용하여, 내 첫 번째 시간 내 tableView 전체 것입니다,하지만 내가 다른보기로 이동하고 다시 tableView를로드하면 비어 있어야합니다 (enqueueOperation이 다시 시작되면 다운로드를 시작하지 않습니다). –

+0

NSURLSession은 다운로드 할 때 앱을 정지시키고 싶지 않기 때문에 사용합니다. 그래서 나는 제 2의 해결책이 나를 위해 충분해야한다고 생각합니다. 하지만 여전히 이상한 다른 NSURLSessionConfiguration 만들기 버그를 수정 찾으십시오, 그건 논리적 인 것 같습니다. –

+0

@ user3282167 새로운 세션을 만드는 것이 효과적인지 궁금합니다. 상상할 수있는 것은 이전 세션을 다시 사용하면 새 요청이 초기 시도에서 보류중인 다운로드 백 로그의 뒤쪽에 대기하고있는 것입니다. 따라서 올바른 대답은 이전 요청을 취소하고 다른 모든 요청 집합을 사용하여 새 세션을 만드는 것이 아니라고 생각됩니다. 내 수정 된 답변을 참조하십시오. – Rob

1

EDIT : 네트워크 트랜잭션을 쉽고 적절하게 관리하기위한 좀 더 일반적인 솔루션을 찾고 있다면 AFNetworking을 확인할 수 있습니다 (더하기 자습서가 많이 있습니다).

나는 모든 경우에 효과가있는 개발을 포기했습니다 (특히 필요하지 않기 때문에 배경 세션 인 경우). 결국 방금 정적 세션이있는 클래스를 만들었습니다. 다운로드 할 대리자를 관리하고 didFinishDownloadingToURL에서 다운로드 한 데이터를 관리하는 데 사용하는 블록을 만들었습니다. 확실히 완벽하지는 않지만 지금은 충분히 좋습니다.

typeof(self) __weak weakSelf = self; 
NSString *downloadUrl = @"http://www.whatyouwant.com"; 
[DownloadManager sharedManager].location = downloadUrl; 
[DownloadManager sharedManager].afterDLBlock = ^(NSURL *location, NSError *error) { 
     weakSelf.regions = [[TeamChoiceManager sharedManager] parseRegions:[NSData dataWithContentsOfURL:location]]; 

     dispatch_sync(dispatch_get_main_queue(), ^{ 
      [weakSelf.activityViewIndicator stopAnimating]; 
      [weakSelf.tableView reloadData]; 
     }); 
}; 
[[DownloadManager sharedManager] downloadTask]; 

내가 여전히 오류 및 위임하지만 taht를 솔루션으로 나는이 없습니다를 관리 할 수있다 :이 클래스와

typedef void (^CTCompletionBlock)(NSURL *location, NSError* err); 

    @interface DownloadManager : NSObject <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDownloadDelegate> 

    @property (nonatomic, retain) NSURLSessionDownloadTask *dlTask; 
    @property (nonatomic, retain) NSString *location; 
    @property (strong) CTCompletionBlock afterDLBlock; 

    + (DownloadManager *)sharedManager; 
    -(void)downloadTask; 
    @end 


    // 
    // DownloadManager.m 
    // MVCTest 
    // 
    // Created by 
    // 

    #import "DownloadManager.h" 
    #import "AppDelegate.h" 


    static DownloadManager *instance = nil; 
    static NSURLSession *session = nil; 

    @implementation DownloadManager 

    + (DownloadManager *)sharedManager { 
     if (instance == nil) { 
      //session = [DownloadManager sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil]; 
      instance = [DownloadManager new]; 
     } 
     return instance; 
    } 

    + (id)new 
    { 
     return [[self alloc] init]; 
    } 
    - (id)init{ 
     self = [super init]; 
     session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] 
     return self; 
    } 

    -(void)downloadTask{ 
     self.dlTask = [session downloadTaskWithURL:[NSURL URLWithString:self.location]]; 
     [self.dlTask resume]; 
    } 

    #pragma mark - NSURLSessionDownloadDelegate 
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { 

NSError *error; 
     if (self.afterDLBlock){ 
      self.afterDLBlock(location, error); 
     } 

    } 

//i still have to manage the delegate... 
    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {} 

    - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didResumeAtOffset:(int64_t)fileOffset expectedTotalBytes:(int64_t)expectedTotalBytes {} 

    #pragma mark - NSURLSessionTaskDelegate 

    - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {} 

    -(void)URLSession:(NSURLSession *)session didBecomeInvalidWithError:(NSError *)error{} 

    #pragma mark - NSURLSessionDelegate 

    - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {} 

    @end 

, 난 그냥 데이터가 다운로드 관리하려면이 코드를 작성해야 귀하의 답변을 주셔서 감사합니다. 일부 데이터를 다운로드하기 위해 많은 코드를 작성하십시오.