2015-09-30 173 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