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

What is the time delay between getting data and loading to UITableView

我正在从 Api 调用中加载我的 UITableView,但尽管数据检索速度相当快,但在将其加载到 table 之前存在明显的时间延迟。使用的代码如下

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
}

当class为运行时,它先命中断点1,然后命中断点2,然后快速转到断点3,然后再次转到断点2。然后在它到达 cellForRowAtIndexPath() 中的断点 4 之前有大约 20 到 30 秒的延迟,并且数据被加载到 UITableView 中。之后视图很快显示出来。

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

table 视图应在您调用 tableView.reloadData() 后几分之一秒内开始重新加载。

但是,如果您从后台线程进行 UI 调用,结果为 "undefined"。在实践中,我看到的一个常见影响是 UI 更改需要非常长的时间才能真正生效。第二个最可能的副作用是崩溃,但其他奇怪的副作用也是可能的。

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

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

在您的情况下,您希望包装标有 "breakpoint 3" 的 3 行代码(所有 UI 调用)以及对 self.alertView()[=16 的其他调用=]

请注意,如果您确定完成闭包中的代码很快,您可以简单地将闭包的整个主体包装在对 dispatch_async(dispatch_get_main_queue()).

的调用中

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

简而言之,您需要用

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

最终结果会是

前 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")
}

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