2

我正在爲iOS應用程序編寫網絡類。該課程負責所有日誌記錄和網絡流量。我遇到了一個問題,我必須一次發送數千個請求,但是NSURLConnections會超時,因爲直到所有NSURLConnections都啓動後,纔會調用委託方法,屆時超時期限屆滿。我爲Drupal使用了一個休息API,不幸的是,我不知道用一個請求創建多個實例的方法。我怎樣才能同時發送回覆呢?如果我使用GCD來關閉NSURLConnections的創建,那麼是否可以解決問題?我想我必須通過遍歷對象的整個操作來發送和發送給GCD,以釋放主線程以迴應響應。爲什麼NSURLConnection在發送很多請求時會超時?

-(BOOL)sendOperation:(NetworkOperation)op 
    NetworkDataType:(NetworkDataType)dataType 
      JsonToSend:(NSArray *)json 
      BackupData:(NSArray *)data 
{ 
    if(loggingMode) 
    { 
     return YES; 
    } 
    NSURLConnection *networkConnection; 
    NSData *send; 
    NSString *uuid = [self generateUUID]; 
    NSMutableArray *connections = [[NSMutableArray alloc] init]; 
    NSMutableURLRequest *networkRequest; 
    for (int i=0; i<[json count] && (data ? i<[data count] : YES); i++) 
    { 
     if(op == Login) 
     { 
      /*Grab all cookies from the server domain and delete them, this prevents login failure 
      because user was already logged in. Probably find a better solution like recovering 
      from the error*/ 
      NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL: 
           [[NSURL alloc] initWithString:networkServerAddress]]; 
      for (NSHTTPCookie *cookie in cookies) 
      { 
       [[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie]; 
      } 
      networkRequest = [[NSMutableURLRequest alloc] initWithURL: 
          [NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/user/login"]]]; 
     } 
     else if(op == StartExperiment) 
     { 
      networkRequest = [[NSMutableURLRequest alloc] initWithURL: 
           [NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/node"]]]; 
     } 
     else if(op == Event || op == EndExperiment || op == SendAll) 
     { 
      networkRequest = [[NSMutableURLRequest alloc] initWithURL: 
           [NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/node"]]]; 
     } 
     else if(op == Logout) 
     { 
      networkRequest = [[NSMutableURLRequest alloc] initWithURL: 
           [NSURL URLWithString:[networkServerAddress stringByAppendingString:@"/user/logout"]]]; 
     } 

     send = [[json objectAtIndex:i] dataUsingEncoding:NSUTF8StringEncoding]; 
     //Set the headers appropriately 
     [networkRequest setHTTPMethod:@"POST"]; 
     [networkRequest setValue:@"application/json"  
       forHTTPHeaderField: @"Content-type"]; 
     [networkRequest setValue:[NSString stringWithFormat:@"%d", [send length]] 
       forHTTPHeaderField:@"Content-length"]; 
     [networkRequest setValue:@"application/json"  
       forHTTPHeaderField:@"Accept"]; 
     //Set the body to the json encoded string 
     [networkRequest setHTTPBody:send]; 
     //Starts async request 
     networkConnection = [[NSURLConnection alloc] initWithRequest:networkRequest delegate:self]; 
     //Successfully created, we are off 
     if(networkConnection) 
     { 
      [networkConnectionsAndData setValue:[[NSMutableArray alloc] initWithObjects:uuid, 
               [[NSNumber alloc] initWithInt:op], [[NSNumber alloc] initWithInt:dataType], [[NSMutableData alloc] init], (data ? [data objectAtIndex:i] : [NSNull null]), nil] 
             forKey:[networkConnection description]]; 
     } 
     else //Failed to conn ect 
     { 
      NSLog(@"Failed to create NSURLConnection"); 
      return NO; 
     } 
    } 
    [[self networkOperationAndConnections] setObject:[[NSMutableDictionary alloc] initWithObjectsAndKeys:[[NSMutableArray alloc] initWithObjects:connections, nil], @"connections", [[NSMutableArray alloc] init], @"errors", nil] 
               forKey:uuid]; 
    return YES; 
} 

的詞典是用於跟蹤與每個NSURLConnection的所述相關數據的和也以組NSURLConnections在一起成爲一個組,以確定整個操作的最終成功或失敗。

更新

AFNetworking在完成這個項目的關鍵。它不僅大大清理了代碼,還處理了發送這麼多請求時繼承的所有線程問題。更不用說AFNetworking,我可以將所有請求一起分批到一個操作中。像AFNetworking使用塊一樣,使用塊是比NSURLConnections的標準代表更清潔和更好的解決方案。

回答

5

您絕對需要允許NSURLRequest/Connection在另一個線程上運行。 (不是主線!)

爲清晰起見進行了編輯**: 我注意到您對「//Starts async request」的評論,並且我想確保您意識到您的電話並非您期望的典型「異步」 「功能。真的,它只是同步發射請求,但由於它的Web請求,它本質上表現爲異步。您希望將這些請求實際放置在另一個線程上以實現完全異步行爲。

其他一切不談,我真的建議深入挖掘蘋果的網絡示例項目位置:MVCNetworking

至於你的問題的細節,有一對夫婦方法可以做到這一點。

  1. 一種是使用initWithRequest:<blah> delegate:<blah> startImmediately:FALSE開始立即讓你的連接,然後使用安排在另一個線程的運行循環您NSURLConnection實例:scheduleInRunLoop:forMode:
    • (注:然後你必須揭開序幕通過調用啓動連接 - 這是最好的通過NSOperation + NSOperationQueue做到這一點)
  2. 或使用NSURLConnection此靜態方法來創建/啓動連接,而不是做一個分配/初始化的:sendAsynchronousRequest:queue:completionHandler:
    • (注意:這種方法與上述方法完全相同,但是混淆了細節並且把一些控制權交給了你的手。)

說實話我快速解答以上將不足以完成這種項目,你就需要做一些研究,以填補空白,尤其是對NSOperationQueue ,這就是MVCNetworking項目將幫助你的地方。

網絡連接是一個善變的野獸 - 即使它們只是在後臺線程上運行,您也可以超時並終止連接,只需嘗試同時執行太多工作即可!我會認真考慮重新開放數千個NSURLConnections,並且使用NSOperationQueue將有助於解決這個問題。

其他資源: 這裏有一個第三方庫,可能使你的網絡冒險痛苦少:

+0

我是初始化時的印象,它正在那裏開始一個異步請求。所以它不是一個異步請求,除非你使用sendAsynchronousRequest?謝謝你們的快速響應。 – 2012-01-27 22:14:13

+0

對不起,我在那裏不是很清楚。這裏是編輯: 我注意到你對「'//啓動異步請求」的評論「,我想確保你意識到你的調用並不是你期望的典型」異步「功能。真的,它只是同步發射請求,但由於它的Web請求,它本質上表現爲異步。您希望將這些請求實際放置在另一個線程上以實現完全異步行爲。 – MechEthan 2012-01-30 17:27:05

+0

添加鏈接到我見過的第三方庫,可以幫助你在這裏推薦 – MechEthan 2012-01-30 19:56:33