2016-09-02 2 views
0

Я разрабатываю приложение для чата, и я настроил следующий механизм для загрузки пользователями сообщений. В принципе, я выталкиваю сообщения в очередь и загружаю их один за другим. Когда очередь пуста, я вызываю finishedUploading, которая запускается каждую секунду и повторяет задачу, если в очереди есть что-то.Загрузка данных с использованием NSURLSession и очереди

var uploadQueue:[UploadMessage]? 
let session = NSURLSession.sharedSession() 
let lockQueue = dispatch_queue_create("com.dsdevelop.lockQueue", nil) 

// RETURNS AMOUNT OF ITEMS STILL IN QUEUE 

func getRemainingActiveUploads() -> Int { 
return (self.uploadQueue != nil) ? self.uploadQueue!.count : 0 
} 

//REMOVES MESSAGE FROM QUEUE ONCE UPLOADED 

func removeMessageFromUploadQueue(messageToBeRemoved : UploadMessage) { 
if (uploadQueue != nil) { 
    dispatch_sync(lockQueue) { 
     self.uploadQueue = self.uploadQueue?.filter({$0.date!.compare(messageToBeRemoved.date!) == NSComparisonResult.OrderedSame}) 
    } 
} 
} 

var uploadTimer : NSTimer? 

// CALLED ONLY WHEN UPLOADQUEUE IS EMPTY, RERUNS THE UPLOAD FUNCTION AFTER 1 SECOND 
func finishedUploading() { 
uploadTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(uploadAllLinks), userInfo: nil, repeats: false) 
if (needToRefetch) { 
    needToRefetch = false 
    newMessageReceived() 
} 
} 

func uploadAllLinks() 
{ 
uploadTimer?.invalidate() 
uploadTimer = nil 
// suspending queue so they don't all finish before we can show it 
session.delegateQueue.suspended = true 
session.delegateQueue.maxConcurrentOperationCount = 1 

let myUrl = NSURL(string: "http://****") 

// create tasks 
if (uploadQueue != nil) { 
    if (uploadQueue?.count > 0) { 
    for message in uploadQueue! 
    { 
     let request = NSMutableURLRequest(URL:myUrl!) 
     request.HTTPMethod = "POST" 
     request.timeoutInterval = 10 
     request.HTTPShouldHandleCookies=false 

     var postString = "sender=" + message.sender! 
     request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding); 

     let dltask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) in 
      if data != nil 
      { 
       do { 
        let jsonArray = try NSJSONSerialization.JSONObjectWithData(data_fixed!, options:[]) 
        dispatch_async(dispatch_get_main_queue(), { 

         if let errorToken = jsonArray["error"] as! Bool? { 
          if !errorToken { 
           self.uploadQueue = self.uploadQueue!.filter({$0.date!.compare(message.date!) != NSComparisonResult.OrderedSame}) 
              let remaining = self.getRemainingActiveUploads() 
              print("Downloaded. Remaining: \(remaining)") 
              if (remaining == 0) { 
               self.finishedUploading() 
              } 
          } 
          else { 

              let remaining = self.getRemainingActiveUploads() 
              print("Downloaded. Remaining: \(remaining)") 
              if (remaining == 0) { 
               self.finishedUploading() 
              } 
          } 
         } 
         else { 

             let remaining = self.getRemainingActiveUploads() 
             print("Downloaded. Remaining: \(remaining)") 
             if (remaining == 0) { 
              self.finishedUploading() 
             } 
         } 

        }) 
       } 
       catch { 
        print("Error: \(error)") 
       } 
      } 

     }) 
     print("Queuing task \(dltask)") 
     dltask.resume() 
    } 
     session.delegateQueue.suspended = false 
    } 
    else { 
     finishedUploading() 
    } 
    // resuming queue so all tasks run 
} 

} 

Теперь это прекрасно работает в следующих двух случаях:

  1. Очередь пуста ->finishedUploading вызывается и uploadAllLinks запускается каждый второй для проверки элементов в uploadQueue
  2. очередь имеет один элемент -> один предмет отправлен, remaining == 0 следовательно finishedUploading называется

Однако, когда бы то ни было очередь имеет более одного элемента, первый загружается, if remaining == 0 терпит неудачу, а затем ничего не происходит. Я не понимаю, почему цикл for не запускается для остальных элементов очереди в этот момент.

ответ

1

Я подозреваю, что проблема заключается в 10-секундном интервале таймаута. Это начинает тикать, как только задача данных создана и завершает задачу, если она остается бездействующей (без получения новых данных) более десяти секунд.

Если у вас есть несколько задач, и OS может загружать только одну или две из них за один раз, любая задача, стоящая в очереди, ожидающая начала, никогда не будет завершена. Я не думаю, что в документации упоминается это.

На практике этот дизайн делает очередность NSURLSession менее идеальной, и, как результат, большинство людей, похоже, сами создают свои очереди и самостоятельно обрабатывают ограничение параллелизма, гарантируя, что каждая задача создается непосредственно перед ее запуском , Я бы предложил сделать что-то подобное:

  • Создайте метод, который начнет следующую загрузку в очереди или вызовет метод «все завершено», если очередь пуста - в основном тело вашего цикла.
  • Вместо самого цикла вызовите этот метод, чтобы начать первую загрузку.
  • В обработчике завершения (внутри этого метода) вызовите этот метод полурекурсивно, чтобы начать следующую загрузку.

Кроме того, 10 секунд слишком короткий для интервала таймаута, если ваше устройство не установлено на стене и не подключено к Wi-Fi с гарантированным сплошным сигналом. Flaky Wi-Fi и слабые сотовые сигналы могут привести к серьезной задержке, поэтому IIRC по умолчанию составляет 120 секунд, хотя я читал 60 в разных местах. В любом случае, вы делаете не хотите использовать 10 секунд. Такой короткий тайм-аут в значительной степени гарантирует, что ваше приложение будет безнадежно ненадежным.

+0

Как и внутри цикла for, переменная запроса не получает повторной инициализации для каждого элемента в очереди; поэтому десятисекундный интервал применим только к одному элементу в очереди? – Alk

+0

Это так, но каждый элемент имеет десятисекундный тайм-аут, который начинается через несколько микросекунд после предыдущего. Цикл for просто создает задачи сразу после следующего и запускает их, потому что вы не дожидаетесь завершения каждой задачи до начала следующей задачи. – dgatwood

+0

Возможно, что я ошибаюсь в том, что интервал тайм-аута является причиной сбоя, но в любом случае это определенно вызовет довольно последовательные сбои в сотовой сети, поэтому вы не должны этого делать. – dgatwood

 Смежные вопросы

  • Нет связанных вопросов^_^