我有一个UITableViewController,必须在启动时加载不太大量的数据。在我的viewDidLoad中,我使用其他队列发送请求:

override func viewDidLoad() {

    super.viewDidLoad()
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
        var data = self.getStoresData()
        dispatch_async(dispatch_get_main_queue(), {
            self.parseStoresData(data)
            self.resultsController = PartnerStore.getAllStores()

        });
    });
}


这些是方法:

func getStoresData() -> [NSDictionary]
{
    var responseData = [NSDictionary]()
    self.refreshControl.beginRefreshing()
    AppDelegate.appDelegate().httpRequestOperationManager?.GET(
        "partner_stores/",
        parameters: nil,
        success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) in
            self.tableView.reloadData();
            println("RESPONSE OBJECT IN GET PARTNER STORES: \(responseObject)") },
        failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
            println("FAIL IN GET PARTNER STORES: \(error)") })
    self.refreshControl.endRefreshing()
    return responseData
}

func parseStoresData(storesData: [NSDictionary])
{
    for storeDict in storesData {
// just inserts a new object to CoreData
        PartnerStore.addNewStore(storeDict)
    }
}


问题是(我认为)API调用是异步的,因此在从服务器下载数据之前,将执行dispatch_async中的两个函数。但是,如果我将所有内容放入GET调用的成功块中,则将花费大量时间,整个UI都将被阻塞。在不阻塞UI线程的情况下进行服务器调用的最佳方法是什么?

最佳答案

您对在dispatch_async请求完成之前被调用的viewDidLoadGET中的两个调用是正确的。那是个问题。当您说您尝试将所有内容放入success块时,您也有一个正确的想法。那才是明智的选择。不过,还有一些其他事项需要移动。

处理UI更新的一种好方法是为UI更新和数据获取提供单独的功能。这样做意味着我们需要将回调传递给您的getStoresData函数,然后在您的GET函数的successerror块中适当地调用它。这将让我们知道何时完成数据提取,以便我们可以完成UI更新。我们还希望将调度从viewDidLoad移到getStoresData到后台队列。

因此,让我们从getStoresData中拉出任何UI更改并移动该调度:

func getStoresData(callback: (error: NSError?) -> Void) {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
        // ... do any setup ...
        AppDelegate.appDelegate().httpRequestOperationManager?.GET(
            // ... other GET parameters ...
            success: { (operation: AFHTTPRequestOperation!, responseObject: AnyObject!) in
                var responseData = [NSDictionary]()

                // do what you need to convert responseObject to responseData
                // then...

                // NOTE: we'll dispatch the the main thread here because parseStoresData deals with CoreData.
                // This dispatch could be done in parseStoresData itself but
                // a callback function would need to be added to it as well
                // in that case.
                dispatch_async(dispatch_get_main_queue(), {
                    self.parseStoresData(responseData)

                    // The response has been dealt with, so call the callback
                    callback(error: nil)
                });
            },
            failure: { (operation: AFHTTPRequestOperation!, error: NSError!) in
                // There was an error, so call the callback with the error object
                callback(error: error)
            }
        )
    })
}


现在,让我们使用新功能来处理UI更新,以便将数据更新与UI更新分离。在此功能中,我们将首先启动刷新控件并调用getStoresData。然后,当getStoresData完成时,更新表视图并停止刷新控件。

func reloadData() {
    // start the refresh control on the main thread so the user knows we're updating
    dispatch_async(dispatch_get_main_queue(), {
        self.refreshControl.beginRefreshing()
    })

    // do the actual data fetch...
    // (remember this will dispatch to a background thread on its own now)
    getStoresData({
        (error: NSError?) -> Void in

        // since this callback could be called from any thread,
        // make sure to dispatch back to the main UI thread to finish the UI updates
        dispatch_async(dispatch_get_main_queue(), {
            if let actualError = error {
                // update the UI appropriately for the error
            } else {
                // update the data in the table view and reload it
                self.resultsController = PartnerStore.getAllStores()
                self.tableView.reloadData();
            }

            // we're done; stop the refresh control
            self.refreshControl.endRefreshing()
        })
    })
}


这使您的viewDidLoad函数现在非常简单:

override func viewDidLoad() {
    super.viewDidLoad()
    reloadData()
}


这也使实现拉动刷新等操作变得更加容易,因为您可以在用户触发刷新时简单地调用reloadData,而不必在整个位置复制UI更新代码。

10-07 19:13
查看更多