2015-11-25 60 views
2

我正在使用NSURLSession API異步地從互聯網上下載兩種類型的csv數據(稱爲類型A和類型B)的OS X(Yosemite)應用程序。每種類型的csv都有多個請求。每個請求都是自定義類中包含的專用會話。有一個基類請求類和每個類型的子類。 (事後看起來可能不是一個理想的設計,但我認爲我的問題無關緊要)。爲什麼我有時會遇到併發的NSURLSession請求的破壞回覆

該應用程序的構建方式使得每種類型的csv數據都以順序隊列的形式下載。每種類型只有一個請求可以同時處於活動狀態,但這兩種類型都可以同時發生,並且兩者都使用主線程用於委託回調。所有這一切通常很好。

我看到的問題是,有時在交通擁堵的情況下,我得到了「交叉聽力」,即我有時會得到一個迴應B類請求的響應,該類型B請求報告已成功完成,但它包含一些B類CVS線然後有一些類型的A行標記後 - 所以我有時(很少)在我的類型B請求中獲得類型A數據。 (或其他方式)。

基本上,它看起來像Apples API中的「切換」邏輯會對哪個傳入數據包屬於哪個請求/會話感到困惑。兩種不同的請求類型轉到不同的URL,但它們是相關的,可能它們最終都解析爲相同的IP,但我不確定這一點。我想知道,如果它們來自同一個服務器,那麼它們可能與數據包標題有關,從而難以確定它們屬於哪個請求(我在互聯網協議上不夠好,不知道這是否是一個明智的猜測) 。如果是這種情況,那麼解決方案必須確保所有請求都在一個隊列中,以使它們不能同時處於活動狀態,但我不想在執行大型體系結構更改之前,我確信沒有其他解決方法。

我尋找類似的問題,發現這個老問題(Why is my data getting corrupted when I send requests asynchronously in objective c for iOS?)似乎描述了完全相同的問題,但不幸的是它沒有答案。除此之外,我沒有發現任何類似的東西,所以我想我在這裏做了一些愚蠢的事情,但是在我開始改變架構來修復它之前,知道爲什麼會出現這個問題會很好。

有沒有人看過這個,知道什麼原因和解決方法是?

我沒有包含任何代碼,因爲我覺得沒有意義,因爲它似乎是一個架構問題,如果我添加代碼,它將需要很多。不過,如果能幫助我們理解這個問題,我會很樂意補充您的建議。

編輯:

相關(我希望)代碼添加到下面。注意對象只有一個鏡頭。該請求的參數由init方法注入,NSURLSession僅用於單個任務。因此,會話在啓動後失效,並在解析數據後釋放NSMutableData數組。

-(BOOL)executeRequest { 
    NSURLSessionConfiguration *theConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration]; 
    NSURLSession *theSession = [NSURLSession sessionWithConfiguration:theConfig delegate:self delegateQueue:[NSOperationQueue mainQueue]]; 
    NSURLRequest *theRequest = [NSURLRequest requestWithURL:self.queryURL cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:BSTTIMEOUT]; 
    NSURLSessionDataTask *theTask = [theSession dataTaskWithRequest:theRequest]; 
    if(!theTask) { 
     return NO; 
    } 
    [theTask resume]; 
    [theSession finishTasksAndInvalidate]; 

    self.internetData = [NSMutableData dataWithCapacity:0]; 

    return YES; 
} 

-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { 
    [self.internetData appendData:data]; 
    return; 
} 

-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { 
     if((error)||(![self parseData])) 
     { 
      self.internetData = nil; 

      if(!error) { 
       NSDictionary *errorDictionary = @{ NSLocalizedDescriptionKey : @"Parsing of internet data failed", NSLocalizedFailureReasonErrorKey : @"Bad data was found in received buffer"}; 
       error = [NSError errorWithDomain:NSCocoaErrorDomain code:EIO userInfo:errorDictionary]; 
      } 
      NSDictionary* ui = [NSDictionary dictionaryWithObject:error forKey:@"Error"]; 
      [[NSNotificationCenter defaultCenter] postNotificationName:[self failNotification] object:self userInfo:ui]; 
      return; 
     } 

     [[NSNotificationCenter defaultCenter] postNotificationName:[self successNotification] object:self]; 
     return; 
} 
+0

請添加一些代碼,特別是處理數據接收的代碼('-connection:didReceiveData:')。 –

+0

更新:我實現了一個請求隊列,可以將csv請求串行化,以便一次只能有一個活動。這似乎解決了我的問題,並且性能可以接受,因爲請求數量並不那麼高,也沒有時間限制。然而,主要問題(爲什麼會發生這種情況?)仍然未知,因此我不會將此作爲答案發布。 – pco494

+0

我終於找到了解決方案,並將其作爲未來參考的答案發布。 – pco494

回答

0

我終於設法追查我的(愚蠢的)錯誤。爲了將來的參考,這個問題是由於沒有意識到返回的數據不是零終止而引起的。

在大多數情況下,我請求的數據是XML和NSXMLParser類想要一個NSData而無需額外的尾隨零,這樣效果很好。

但偶爾失敗的請求使用CSV格式,其中數據通過NSString,這是由[NSString stringWithUTF8String]創建的,其中期望以零終止的c樣式字符串作爲輸入。這是主要的罪魁禍首。通常它應該如此。有時它直接失敗,有時它只是做了一個緩衝區溢出並獲得了一些在同一個內存區域的請求數據。這些是我在發佈問題時注意到的情況。

因此,解決方案是切換到使用[[NSString alloc] initWithData: encoding:NSUTF8StringEncoding],它與非空終止的NSData緩衝區一起使用。

0

首先:你不應該爲每個請求創建一個新的會話。這不再是會話。從docs

隨着NSURLSession API,你的應用程序創建一個或多個會話,每個座標一組相關的數據傳輸任務。例如,如果您正在編寫Web瀏覽器,則您的應用程序可能會爲每個選項卡或窗口創建一個會話,或者一個會話用於交互式使用,另一個會話用於後臺下載。在每個會話中,您的應用程序會添加一系列任務,每個任務都表示對特定URL的請求(如果需要,請在HTTP重定向之後)。

第二:你在哪裏存儲會話等,所以它不會被釋放?

您的主要問題:很顯然,當請求可能正在運行時,您會啓動新的請求。但是您只有一個NSMutableData實例可以接收-URLSession:task:didReceiveData:中的數據:許多請求,一個存儲......當然這會混合起來。

+0

謝謝您的回覆,但我不完全明白您的意思。我的印象是NSURLSession是建立網絡連接的常用方式。請你澄清一下。此外,我意識到我的問題並不清楚。每個請求都在它自己的對象實例中,它擁有自己的NSURLSession,並且還將NSMutableData緩衝區作爲一個屬性保存,所以我不認爲這些不同的請求會壓制其他每個數據。這些對象只會觸發一次,因此不可能每個請求都發起多個請求。 – pco494

+0

第一部分:請參閱我的編輯。第二個:好吧,我真的沒有意識到每個請求都有一個新的實例。然而,在這種情況下,我會在'-executeRequest'的開始處檢查是否已經有一個正在進行的請求,或者 - 更好地爲實例提供一個完全參數化的初始化器,並立即執行請求。 –

+0

謝謝,是的,我開始請求威脅其他人正在運行,但從來沒有在同一個NSURLSession。文檔似乎表明這是意圖,例如他們在Web瀏覽器中每個選項卡討論一個會話。然而由於某種原因,我得到的答覆感到困惑,以至於我得到應答的一部分,它應該發送到會話B中的會話A.仍然如您所說,我通過爲所有對象實現順序隊列來解決此問題。仍然爲什麼答覆的混淆發生仍然不清楚我會調查,如果我不小心可能創建一些共享會話的引用。 – pco494

相關問題