Service API调用阻止UITableViewController中的UI

时间:2014-08-12 14:57:00

标签: ios swift afnetworking

我有一个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线程的情况下,服务器调用的最佳方法是什么?

1 个答案:

答案 0 :(得分:1)

dispatch_async请求完成之前,viewDidLoad GET中的两个来电是正确的。这是一个问题。当你说你试图把所有东西放在success区块时,你也有正确的想法;应该顺其自然的地方。还有一些其他的事情需要移动。

处理UI更新的一个好方法是为UI更新和数据提取分别提供功能。这样做意味着我们需要将回调传递给您的getStoresData函数,然后在GET函数success和{{1}中正确调用它块。这将告诉我们数据提取何时完成,以便我们可以完成UI更新。我们还希望将调度移至error之外的后台队列,然后移至viewDidLoad

因此,我们可以从getStoresData中提取任何UI更改并移动该调度:

getStoresData

现在让我们使用新功能来处理更新UI,以便将数据更新与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) } ) }) } 。然后,当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() } ,而不是在整个地方复制UI更新代码。