2015-09-30 119 views
0

我的Swift應用程序出現了一個奇怪的問題,其中看似順序的事件序列導致了無序輸出。我有一個按鈕,點擊操作,看起來像以下(其中self.indicator是UIActivityIndi​​catorView和self.errorBox是一個UILabel):對NSURLSession響應的延遲反應

func onSigninClick(sender: UIButton!) { 

    let url = NSURL_REMOVED_FROM_EXAMPLE 
    var request = NSMutableURLRequest(URL: url!) 
    request.HTTPMethod = "POST" 

    request.addValue("application/json", forHTTPHeaderField: "Content-Type") 
    request.addValue("application/json", forHTTPHeaderField: "Accept") 

    var data = [ 
     "u": self.usernameBox.text, 
     "p": self.passwordBox.text 
    ] 
    var err: NSError? 
    request.HTTPBody = NSJSONSerialization.dataWithJSONObject(data, options: nil, error: &err) 

    let config = NSURLSessionConfiguration.defaultSessionConfiguration() 
    let session = NSURLSession(configuration: config) 

    UIApplication.sharedApplication().networkActivityIndicatorVisible = true 
    self.indicator.startAnimating() 
    self.errorBox.text = "" 

    session.dataTaskWithRequest(request, completionHandler: { 
     (data, response, error) in 

     self.indicator.stopAnimating() 
     UIApplication.sharedApplication().networkActivityIndicatorVisible = false 

     if error != nil { 
      Logger.warn("Sign In Error: \(error.localizedDescription)") 
     } else { 
      let json = Parser.parseJson(data) // returns SwiftyJSON object 
      Logger.info("Received response: \(json.stringValue)") 
      if json["result"].boolValue { 
       // success 
       self.dismissViewControllerAnimated(true, completion: nil) 
       NSNotificationCenter.defaultCenter().postNotificationName(Account.loginNotification, object: self) 
      } else { 
       // additional errors within message 
       for error in json["errors"]["details"].arrayValue { 
        let code = error["code"].intValue 
        let message = error["message"].stringValue 
        Logger.error("Internal error \(code): \(message)") 

        if code == 1001 { 
         self.errorBox.text = "You must specify username and password" 
        } else if code == 1002 { 
         self.errorBox.text = "Incorrect username or password" 
        } else { 
         self.errorBox.text = "Unknown error, error code: \(code)" 
        } 
       } 
      } 
     } 
    }).resume() 
} 

我發送請求和接收響應。發送請求時,會出現一個旋轉指示符,並且一旦收到該消息,意圖就會使其消失。

問題是,在UI「反應」之前,請求使它回到原狀。在我上面的邏輯中,您可以看到日誌消息。一旦消息出現在日誌中,我也會看到UIApplication.sharedApplication().networkActivityIndicatorVisible圖標消失。然而,該指標(被告知停止動畫之前)繼續旋轉5秒左右。這同樣適用於僅在約5秒後出​​現的錯誤消息。我也在我的loadView中定義了self.indicator.hidesWhenStopped = true

我無法弄清楚發生了什麼事。爲什麼指標和錯誤框都沒有更新,直到很久以後呢?即使重畫是異步的,也不需要5秒鐘的時間。

此外,我試着計時,延遲似乎也有所不同。有時爲5秒,有時長達30個。

回答

2

dataWithRequest:completionHandler:方法提供的完成處理程序將在NSURLSessiondelegateQueue上調用。默認情況下,此隊列是爲NSURLSession創建的串行NSOperationQueue,即不是主線程。即使這樣,我也不確定iOS是否會爲UI更新發生的任何行爲保證,如果它們是在主線程之外發起的。

在iOS中,UI操作必須發生在主線程上。當您告訴您的活動指示器停止動畫時,只有主線程發現更新時纔會發生任何動作。

嘗試設置的delegateQueueNSURLSession到主隊列,或者你可以嘗試使用dispatch_async主線程只是用戶界面的更新:

self.indicator.stopAnimating() 
UIApplication.sharedApplication().networkActivityIndicatorVisible = false 

編輯顯示delegateQueue方法:

雖然只讀delegateQueue屬性,您可以創建NSURLSession與專門init方法時設置:

init(configuration configuration: NSURLSessionConfiguration, 
    delegate delegate: NSURLSessionDelegate?, 
    delegateQueue queue: NSOperationQueue?) 
+0

謝謝! NSURLSession的delegateQueue是隻讀的,所以這個選項已經結束,但是dispatch_async起作用了。 –

+0

delegateQueue屬性是隻讀的,但您可以使用專門的init方法對其進行設置。 – Tim