2013-03-28 29 views
0

我正在開發一個靜態庫,它需要在後臺執行一些操作,而不需要與主線程交互。爲了給你一個想法,可以考慮記錄一些用戶事件。圖書館必須繼續做這些東西,直到用戶退出應用程序或將其發送到後臺(按下主頁按鈕) - 換句話說,它需要繼續在循環內執行某些操作。在NSThread或NSOperation中使用NSUrlConnection

主應用程序線程和衍生線程之間的唯一交互是偶爾主應用程序線程會將一些東西(一個事件對象)放入一個產生線程可讀取/消耗的隊列中。除此之外,產生的線程繼續前進,直到應用程序存在或背景。

生成的線程需要做的一部分(儘管不是全部)涉及將數據發送到HTTP服務器。我會認爲,將NSThread子類化,覆蓋它的主要方法,並且在該連接上以某種超時對NSUrlConnection進行同步調用很容易,因此線程不會永久掛起。例如,在Java/Android中,我們只是子類Thread,重寫start()方法並調用同步的HTTP GET方法(例如,從Apache的HttpClient類中調用)。這很容易,並且工作正常。但是從我在這裏和其他地方看到的情況來看,顯然在iOS上它比這更復雜,而且我對實際工作的最佳方法有點困惑。

那麼我應該繼承NSThread並以某種方式使用NSUrlConnection?看起來異步的NSUrlConnection在NSThread中不起作用,因爲委託方法不會被調用,但是同步方法呢?我是否需要使用和配置RunLoop並設置autorelease池?或者我應該使用NSOperation?在我看來,我試圖做的事很常見 - 有沒有人有一個正確的做法的工作示例?

回答

1

據我所知,要異步使用NSURLConnection,你需要一個runloop。即使你使用NSOperation,你仍然需要一個runloop。

我見過的所有例子都使用Main Thread來啓動NSURLConnection,它有一個runloop。使用NSOperation的例子設置,使操作Concurrent告訴NSOperationQueue不提供它自己的線程,他們然後確保NSURLConnection通過電話開始在主線程,例如performSelectorOnMainThread:

下面是一個例子:

Pulse Engineering Blog: Concurrent Downloads using NSOperationQueues

您也可以搜索QRunLoopOperation蘋果文檔的LinkedImageFetcher樣品是顯示這種事情的一些來龍去脈的示例類英寸

(雖然我不知道我真的看到任何代碼示例展示瞭如何經營自己的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]; 
     }); 
} 

的doStuffInBackground方法在此時後臺運行,所以你可以只使用NSURLConnection的同步。在我這裏的例子中,該方法循環進行網絡調用,直到可能的其他代碼集backgroundStuffShouldRun = false。網絡通話時間爲10秒。通話結束後,我正在更新UI標籤以顯示進度。請注意,UI更新是使用「dispatch_async(dispatch_get_main_queue()...」)執行的。這將根據需要在UI線程上運行UI更新。

此背景工作存在一個潛在問題:無法取消http請求本身。不過,與10秒的短暫停,你會等待10秒的最大的線程局外人(可能有些事件在UI)後中止本身設置backgroundStuffShouldRun =假。

- (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 
    } 

} 
相關問題