2013-03-28 2 views
0

주 스레드와 상호 작용하지 않고 백그라운드에서 작업해야하는 정적 라이브러리를 개발 중입니다. 아이디어를 얻으려면 일부 사용자 이벤트 만 로깅하는 것이 좋습니다. 라이브러리는 사용자가 앱을 종료하거나 배경으로 보냅니다 (홈 버튼을 밀기 전까지)이 작업을 계속해야합니다. 즉 루프 내에서 계속 작업해야합니다.NSThread 또는 NSOperation 내부에서 NSUrlConnection 사용

메인 앱 스레드와 스폰 된 스레드 간의 유일한 상호 작용은 메인 앱 스레드가 스폰 된 스레드가 읽고 소비 할 수있는 대기열에 일정 항목 (이벤트 객체)을 넣는 경우가 종종 있습니다. 그 이외에, 스폰 된 스레드는 앱이 존재하거나 배경이 될 때까지 계속 진행됩니다.

스폰 된 스레드가 수행해야하는 작업 (일부는 아니지만)에는 HTTP 서버에 데이터를 보내는 작업이 포함됩니다. 나는 NSThread를 서브 클래스 화하고 main 메소드를 오버라이드 (override)하는 것이 쉬우 며, NSUrlConnection에 대한 동기 호출을 그 연결에 대한 어떤 종류의 타임 아웃과 함께 만들어서 스레드가 영원히 멈추지 않을 것이라고 생각했다. 예를 들어 Java/Android에서는 Thread를 서브 클래스 화하고 start() 메소드를 대체하고 동기 HTTP GET 메소드 (Apache의 HttpClient 클래스)를 호출한다. 이것은 매우 쉽고 잘 작동합니다. 그러나 내가 여기에서 보았던 것과 다른 곳에서, 분명히 iOS에서 이것은 훨씬 더 복잡하며, 나는 최선의 접근 방식이 실제로 작동한다는 것에 대해 다소 혼란 스럽다.

그래서 NSThread를 하위 클래스로 만들고 NSUrlConnection을 사용해야합니까? 그것은 비동기 NSUrlConnection NSThread 내부에서 작동하지 않습니다 대리인 메서드를 호출하지 않기 때문에하지만 동기식 메서드는 어떨까요? 어떻게 든 RunLoop을 사용하고 구성하고 자동 풀 풀을 설정해야합니까? 아니면 NSOperation을 사용해야합니까? 내가하는 일은 꽤 흔한 일 인 것 같다. 아무도 제대로 할 방법에 대한 실례를 가지고 있는가?

답변

1

제가 이해 하기론 NSURLConnection을 비동기 적으로 사용하려면 런 루프가 필요합니다. NSOperation을 사용해도 여전히 런 루프가 필요합니다.

내가 본 모든 예는 Main Thread을 사용하여을 실행 루프 루프로 사용합니다. NSOperation를 사용하는 예로는 작업이 NSOperationQueue는, 그들은 다음 NSURLConnectionperformSelectorOnMainThread: 다음

에 대한 호출을 통해 예를 들어, 주 스레드에서 시작되었는지 확인 자신의 스레드를 만들 것 제공하지 않는 지시 Concurrent입니다 설정 예입니다된다 :

Pulse Engineering Blog: Concurrent Downloads using NSOperationQueues

또한 이런 종류의 일부 기능과 아웃을 보여주는 예제 클래스 인 LinkedImageFetcher 샘플 QRunLoopOperation에 대한 애플의 문서를 검색 할 수 있습니다.

은 (내가 실제로 예를 들어 자신의 runloop를 실행하는 방법을 보여주는 것으로 코드를보고 확실하지 않습니다 만, 다시이 예는 기본 스레드를 사용합니다.)

0

나는 그랜드 센트럴 디스패치를 ​​사용했습니다 (GCD) 방법을 사용합니다. 다음은 간단한 테스트 응용 프로그램에서 저에게 도움이되는 예제입니다 (정적 라이브러리에 적용되는지는 잘 모르겠지만 한 번 볼 가치가 있음). ARC를 사용하고 있습니다.

예를 들어, 필자는 viewDidLoad 메서드에서 배경 작업을 시작했지만 어느 곳에서나 시작할 수 있습니다. 핵심은 "dispatch_async (dispatch_get_global_queue ...")는 백그라운드 스레드에서 블록을 실행한다는 것입니다.이 방법의 좋은 설명이 답변을 참조하십시오 : https://stackoverflow.com/a/12693409/215821

여기 내 viewDidLoad에 있습니다 :

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), 
     ^(void) { 
      [self doStuffInBackground]; 
     }); 
} 

그냥 기적으로있는 NSURLConnection을 사용할 수 있도록 doStuffInBackground 방법은,이 시점에서 백그라운드에서 실행됩니다. 위의 예에서이 메소드는 backgroundStuffShouldRun = false를 설정하는 다른 코드가있을 때까지 네트워크 호출을 반복합니다. 네트워크 호출은 10 초의 시간 초과로 이루어집니다. 전화가 오면 진행 상황을 보여주기 위해 UI 라벨을 업데이트합니다. UI 업데이트는 "dispatch_async (dispatch_get_main_queue() ...")로 수행되며 필요에 따라 UI 스레드에서 UI 업데이트가 실행됩니다. http 요청 자체. 그러나 10 초 제한 시간이 지나면 외부 사용자 (UI의 일부 이벤트)가 backgroundStuffShouldRun = false를 설정 한 후 스레드가 중단되도록 최대 10 초 동안 대기하게됩니다.

- (void)doStuffInBackground 
{ 
    while (backgroundStuffShouldRun) { 
     // prepare for network call... 
     NSURL* url = [[NSURL alloc] initWithString:@"http://maps.google.com/maps/geo"]; 

     // set a 10 second timeout on the request 
     NSURLRequest* request = [[NSURLRequest alloc] initWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:10]; 

     NSError* error = nil; 
     NSURLResponse *response = nil; 

     // make the request 
     NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 

     // were we asked to stop the background processing? 
     if (!backgroundStuffShouldRun) { 
      return; 
     } 

     // process response... 

     NSString* status = @"Success"; 

     if (error) { 
      if (error.code == NSURLErrorTimedOut) { 
       // handle timeout... 
       status = @"Timed out"; 
      } 
      else { 
       // handle other errors... 
       status = @"Other error"; 
      } 
     } 
     else { 
      // success, handle the response body 
      NSString *dataAsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 
      NSLog(@"%@", dataAsString); 
     } 


     // update the UI with our status 
     dispatch_async(dispatch_get_main_queue(), ^{ 
      [statusLabel setText:[NSString stringWithFormat:@"completed network call %d, status = %@", callCount, status]]; 
     }); 

     callCount++; 
     sleep(1); // 1 second breather. not necessary, but good idea for testing 
    } 

}