Какова временная задержка между получением данных и загрузкой в ​​UITableView

Я загружаю свой UITableView из вызова API, но, хотя данные извлекаются довольно быстро, перед их загрузкой в ​​таблицу происходит значительная задержка. Используемый код ниже

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
}

Когда класс запускается, он сначала достигает точки останова 1, затем достигает точки останова 2, затем быстро переходит к точке останова 3, а затем снова переходит к точке останова 2. Затем происходит задержка примерно от 20 до 30 секунд, прежде чем он достигнет точки останова 4 в cellForRowAtIndexPath(), и данные будут загружены в UITableView. После этого вид отображается быстро.

Данные извлекаются из веб-службы довольно быстро, так почему возникает значительная задержка перед загрузкой данных в tableView? Есть ли необходимость использовать метод веб-службы?


person user616076    schedule 03.08.2016    source источник
comment
сколько данных? не должно быть такой большой задержки, можете ли вы просто распечатать временные метки в местах, где у вас есть комментарии точки останова, и показать нам вывод?   -  person Lu_    schedule 03.08.2016
comment
NSURLSession обрабатывает перевод вызова веб-службы в фоновый поток, поэтому вам не нужно об этом беспокоиться. Когда вы нажмете точку останова 3 и перезагрузите таблицу, должна быть вызвана точка останова 2, а затем точка останова 4 несколько раз. Разве это не тот порядок, который происходит?   -  person keithbhunter    schedule 03.08.2016
comment
Да, там пятнадцать записей и данные не большие. После перезагрузки таблицы (точка останова 3) происходит задержка около 20 секунд или более после того, как она снова достигает точки останова 2, прежде чем она достигнет точки останова 4, что странно, что она делает?   -  person user616076    schedule 03.08.2016


Ответы (3)


Вы получаете ответ сервера в фоновом потоке, поэтому вам нужно вызвать функцию reloadData() в потоке пользовательского интерфейса. Я подозреваю, что время ожидания может варьироваться в зависимости от того, взаимодействуете ли вы с приложением, которое эффективно вызывает поток пользовательского интерфейса, и именно тогда таблица фактически отображает новые данные.

Короче говоря, вам нужно обернуть 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()
}

Постсвифт 3.0

DispatchQueue.main.async {
    print("This is run on the main queue, after the previous code in outer block")
}
person Ahmad Baracat    schedule 03.08.2016
comment
OP должен обернуть не только вызов reloadData, но и все другие вызовы пользовательского интерфейса. (Приятный штрих, предоставляющий синтаксис Swift 2 и Swift 3, кстати. Проголосовал только за это.) - person Duncan C; 03.08.2016
comment
Это была проблема, теперь загружается мгновенно, спасибо - person user616076; 03.08.2016

Табличное представление должно начать перезагружаться через доли секунды после вызова tableView.reloadData().

Однако если вы выполняете вызовы пользовательского интерфейса из фонового потока, результаты «неопределены». На практике общий эффект, который я наблюдал, заключается в том, что изменения пользовательского интерфейса вступают в силу через абсурдно долгое время. Вторым наиболее вероятным побочным эффектом является сбой, но возможны и другие, странные побочные эффекты.

Обработчик завершения для вызовов NSURLSession по умолчанию запускается в фоновом потоке. Поэтому вам нужно обернуть все ваши вызовы пользовательского интерфейса вызовом dispatch_async(dispatch_get_main_queue()) (который теперь DispatchQueue.main.async() в Swift 3.)

(Если вы выполняете работу с интенсивными вычислениями, такую ​​как синтаксический анализ JSON в своем закрытии, лучше всего делать это в фоновом режиме, чтобы не блокировать основной поток. Затем выполняйте только вызовы пользовательского интерфейса из основного потока.)

В вашем случае вы хотите обернуть 3 строки кода, отмеченные «точкой останова 3» (все вызовы пользовательского интерфейса), а также другие вызовы self.alertView()

Обратите внимание: если вы уверены, что код в закрытии завершения выполняется быстро, вы можете просто обернуть все тело замыкания вызовом dispatch_async(dispatch_get_main_queue()).

person Duncan C    schedule 03.08.2016

Просто убедитесь, что вы перезагрузили свое табличное представление внутри основного асинхронного Dispatch, сразу же вы получите данные

person Chidi Emeh    schedule 04.11.2017