获取数据和加载到UITableView之间的时间延迟是多少

时间:2016-08-03 13:02:59

标签: ios swift web-services uitableview nsurlsessiondownloadtask

我正在从Api调用中加载我的UITableView,但是虽然数据检索得相当快,但在将其加载到表中之前会有很长的时间延迟。使用的代码如下

import UIKit

class TrackingInfoController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet var table : UITableView?
@IBOutlet var indicator : UIActivityIndicatorView?
@IBOutlet var spinnerView : UIView?

var tableArrayList = Array<TableData>()

struct TableData
{
    var dateStr:String = ""
    var nameStr:String = ""
    var codeStr:String = ""
    var regionStr:String = ""

    init(){}
}

override func viewDidLoad() {
    super.viewDidLoad()

    table!.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
    spinnerView?.hidden = false
    indicator?.bringSubviewToFront(spinnerView!)
    indicator!.startAnimating()
    downloadIncidents()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

@IBAction func BackToMain() {
    performSegueWithIdentifier("SearchToMainSegue", sender: nil)
}

//#pragma mark - Table view data source

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 1     //BreakPoint 2
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return tableArrayList.count;
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("CustomCell") as! CustomTableViewCell

    cell.incidentDate.text = tableArrayList[indexPath.row].dateStr
    cell.incidentText.text = tableArrayList[indexPath.row].nameStr
    cell.incidentCode.text = tableArrayList[indexPath.row].codeStr
    cell.incidentLoctn.text = tableArrayList[indexPath.row].regionStr

    return cell     //BreakPoint 4
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
    AppDelegate.myGlobalVars.gIncName = tableArrayList[indexPath.row].nameStr
    AppDelegate.myGlobalVars.gIncDMA = tableArrayList[indexPath.row].codeStr

    performSegueWithIdentifier("SearchResultsToDetailSegue", sender: nil)
}

func alertView(msg: String) {
    let dialog = UIAlertController(title: "Warning",
                                   message: msg,
                                   preferredStyle: UIAlertControllerStyle.Alert)
    dialog.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))
    presentViewController(dialog,
                          animated: false,
                          completion: nil)
}

func downloadIncidents()
{
    var event = AppDelegate.myGlobalVars.gIncName
    var DMA = AppDelegate.myGlobalVars.gIncDMA
    if event == "Enter Event Name" {
        event = ""
    }
    if DMA == "Enter DMA" {
        DMA = ""
    }
    let request = NSMutableURLRequest(URL: NSURL(string: "http://incident-tracker-api-uat.herokuapp.com/mobile/events?name=" + event)!,
                                      cachePolicy: .UseProtocolCachePolicy,
                                      timeoutInterval: 10.0)
    request.HTTPMethod = "GET"
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        if error != nil {
            self.alertView("Error - " + error!.localizedDescription)
        }
        else {
            do {
                var incidentList: TableData
                if let json = try NSJSONSerialization.JSONObjectWithData(data!, options:.AllowFragments) as? Array<Dictionary<String, AnyObject>> {
                    for item in json {
                        if let dict = item as? Dictionary<String, AnyObject> {
                            incidentList = TableData()
                            if let nameStr = dict["name"] as? String {
                                incidentList.nameStr = nameStr
                            }
                            if let codeStr = dict["dma"] as? String {
                                incidentList.codeStr = codeStr
                            }
                            if let dateStr = dict["supplyOutageStart"] as? String {
                                let tmpStr = dateStr
                                let index = tmpStr.startIndex.advancedBy(10)
                                incidentList.dateStr = tmpStr.substringToIndex(index)
                            }
                            if let regionStr = dict["region"] as? String {
                                incidentList.regionStr = regionStr
                            }
                            self.tableArrayList.append(incidentList)
                        }
                    }
                    self.spinnerView?.hidden = true
                    self.indicator?.stopAnimating()
                    self.table?.reloadData()     //BreakPoint 3
                }
            }catch let err as NSError
            {
                self.alertView("Error - " + err.localizedDescription)
            }
        }
    })
    task.resume()      //BreakPoint 1
}

当类运行时,它首先命中BreakPoint 1,然后命中BreakPoint 2,然后快速转到BreakPoint 3,然后再次转到BreakPoint 2。然后在cellForRowAtIndexPath()中遇到Breakpoint 4之前有大约20到30秒的延迟,并且数据被加载到UITableView中。之后会快速显示视图。

从Web服务中快速检索数据,那么为什么在将数据加载到tableView之前会有明显的延迟?是否需要线程化Web服务方法?

3 个答案:

答案 0 :(得分:2)

在您致电tableView.reloadData()后,表格视图应该会在几分之一秒内开始重新加载。

但是,如果从后台线程进行UI调用,则结果为“未定义”。在实践中,我看到的一个常见效果是UI更改需要花费很长时间才能真正生效。第二个最可能的副作用是崩溃,但其他奇怪的副作用也是可能的。

默认情况下,NSURLSession调用的完成处理程序在后台线程上运行。因此,您需要在调用dispatch_async(dispatch_get_main_queue())(现在在Swift 3中为DispatchQueue.main.async())中包装所有UI调用。)

(如果你在闭包中做JSON解析之类的计算密集型工作,最好从后台执行此操作,这样就不会阻塞主线程。然后从主线程中调用UI调用。)

在您的情况下,您需要包含标有“断点3”(所有UI调用)的3行代码以及对self.alertView()的其他调用

请注意,如果您确定完成闭包中的代码很快,则可以通过调用dispatch_async(dispatch_get_main_queue())来简单地封闭整个闭包。

答案 1 :(得分:2)

您正在后台线程中获得服务器响应,因此您需要在UI线程上调用reloadData()函数。我怀疑等待时间可能会有所不同,具体取决于您是否与应用程序进行交互,这有效地调用了UI线程,并且当表实际显示新数据时就是这样。

简而言之,您需要使用

包装self.table?.reloadData() //BreakPoint 3
dispatch_async(dispatch_get_main_queue()) {
    // update some UI
}

最终结果将是

Pre Swift 3.0

dispatch_async(dispatch_get_main_queue()) {
    self.table?.reloadData()
}

Post Swift 3.0

DispatchQueue.main.async {
    print("This is run on the main queue, after the previous code in outer block")
}

答案 2 :(得分:-1)

只需确保在Dispatch主异步内部重新加载tableview,只需立即获取数据