简体   繁体   中英

Capturing closure values in Swift

My question is very similar to several others here but I just can't get it to work. I'm making an API call via a helper class that I wrote.

First I tried a standard function with a return value and the result was as expected. The background task completed after I tired to assign the result.

Now I'm using a closure and I can get the value back into my view controller but its still stuck in the closure, I have the same problem. I know I need to use GCD to get the assignment to happen in the main queue.

this is what I have in my view controller

var artists = [String]()
let api = APIController()
    api.getArtistList("foo fighters") { (thelist) -> Void in
        if let names = thelist {
            dispatch_async(dispatch_get_main_queue()) {
                artists = names
                print("in the closure: \(artists)")
            }
        }
    }

    print ("method 1 results: \(artists)")

as the results are:

method 1 results: []
in the closure: [Foo Fighters & Brian May, UK Foo Fighters, John Fogerty with Foo Fighters, Foo Fighters, Foo Fighters feat. Norah Jones, Foo Fighters feat. Brian May, Foo Fighters vs. Beastie Boys]

I know why this is happening, I just don't know how to fix it :( The API calls need to be async, so what is the best practice for capturing these results? Based on what the user selects in the table view I'll be making subsequent api calls so its not like I can handle everything inside the closure

I completely agree with the @Craig proposal of the use of the GCD, but as your question involves the request of the API call every time you select a row, you can do the following:

  • Let's suppose you use the tableView:didSelectRowAtIndexPath: method to handle the selection, then you can do the following inside it:

     func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { // it is just a form to get the item let selectedItem = items.objectAtIndex(indexPath.row) as String api.getArtistList(selectedItem) { (thelist) -> Void in if let names = thelist { dispatch_async(dispatch_get_main_queue()) { artists = names } } } } 

And then you can observe the property and handle do you want inside it :

var artists: [String] = [] {
   didSet {
      self.tableView.reloadData() // or anything you need to handle.
   }
}

It just another way to see it. I hope this help you.

The easy solution is to do whatever you're doing at your print() , inside the closure .

Since you're already dispatch_async ing to the main queue (the main/GUI thread), you can complete any processing there. Push a new view controller, present some modal data, update your current view controller, etc.

Just make sure that you don't have multiple threads modifying/accessing your local/cached data that is being displayed. Especially if it's being used by UITableViewDelegate / UITableViewDataSource implementations, which will throw fits if you start getting wishy-washy or inconsistent with your return values.

As long as you can retrieve the data in the background, and the only processing that needs to occur on the main thread is an instance variable reassignment, or some kind of array appending, just do that on the main thread, using the data you retrieved on the back end. It's not heavy. If it is heavy, then you're going to need more sophisticated synchronization methods to protect your data.

Normally the pattern looks like:

dispatch_async(getBackgroundQueue(), {
    var theData = getTheDataFromNetwork();
    dispatch_async(dispatch_get_main_queue() {
        self.data = theData // Update the instance variable of your ViewController
        self.tableView.reloadData() // Or some other 'reload' method
    });
})

So where you'd normally refresh a table view or notify your ViewController that the operation has completed (or that local data has been updated), you should continue your main-thread processing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM