异步执行后的 Swift 2 回调

标签 swift asynchronous swift2 grand-central-dispatch

我正在尝试从 Swift 2 中的服务获取 JSON

https://someapidomain.com/entries.json 
[
1212,
1234,
2934,
....
]

https://someapidomain.com/entry/entry_id.json 
{
  "key1": "value1",
  "key2": 23,
  "key3": "value3,
   ...
}

Service must return:
[
 {
  "key1": "value1",
  "key2": 23,
  "key3": "value3,
   ...
},
{
  "key1": "value1",
  "key2": 23,
  "key3": "value3,
   ...
},
....
]


struct ExampleService {

private static let baseURL = "https://someapidomain.com/"
private static let entries_per_page = 10

private static func getJSONResponse(url: String, callback: (AnyObject) -> () ){

        let request = NSMutableURLRequest(URL: NSURL(string: url)!)
        let session = NSURLSession.sharedSession()

        request.HTTPMethod = "GET"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json", forHTTPHeaderField: "Accept")

        let task = session.dataTaskWithRequest(request, completionHandler: {
            data, response, err -> Void in

            do {
                let jsonResponse = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
                callback(jsonResponse)

            }catch {
                print("Error processing json")
            }

        })
        task.resume()


    }

 static func getEntries(url: String, callback: ([[String: AnyObject]]) -> ()) {

        var entries: [[String:AnyObject]] = []
        let apiURL = baseURL + url

        getJSONResponse(apiUrl) { (response) in
            if let entryIds = response as? Array<Int> {
                for entryId in storyIds[0..<entries_per_page] {
                    let entryURL = baseURL + "/entry/\(entryId).json"

                    getJSONResponse(entryURL) { (response) in

                        if let entry = response as? [String: AnyObject] {

                           print(entry)
                           entries.append(entry)

                        }

                    }

                }

               callback(entries)

            }
        }


    }

}


}

现在当我调用服务时我得到空数组

ExampleService.getEntries("/entries.json") { (response) in
     print(response)  // prints []
}

而循环内的各个条目会打印 json。我认为这是因为在 for 循环执行完成之前调用了回调。

我该如何解决这个问题?

最佳答案

修改后的函数 getEntries 概述了如何使用 dispatch_group 完成此操作。

此外,您必须确保您的函数 getJSONResponse 始终调用完成处理程序,即使在它失败的情况下也是如此。这是因为,您必须确保函数调用 dispatch_enterdispatch_leave 是平衡的。

从这个意义上说,我建议也修改 getEntries 函数,即使它失败,它也会始终调用完成处理程序。相应地修改完成处理程序的签名。

static func getEntries(url: String, callback: ([[String: AnyObject]]) -> ()) {

    var entries: [[String:AnyObject]] = []

    getJSONResponse(url) { (response) in
        let queue = dispatch_queue_create("myqueue", nil)
        let group = dispatch_group_create()
        if let entryIds = response as? Array<Int> {
            for entryId in entryIds {
                dispatch_group_enter(group)
                let entryURL = baseURL + "/entry/\(entryId).json"
                getJSONResponse(entryURL) { (response) in
                    if let entry = response as? [String: AnyObject] {
                        print(entry)
                        entries.append(entry)
                    }
                    dispatch_group_leave(group)
                }
            }
        }
        dispatch_group_notify(group, queue) {
            callback(entries)
        }
    }

}

关于异步执行后的 Swift 2 回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34048363/

相关文章:

ios - 使用通用协议(protocol)过滤 subview 数组

Swift 不能在结构内部使用任何变量

ios - 如何异步加载UITableViewcell图像,以便滚动不会滞后

swift - Swift 中获取数组的 Element 类型(通过反射)

swift - 如何比较两个实际上符合 `Any` 的 `Equatable` 对象

xcode - 我应该将 UIIMageViews 直接放在 Controller 的 View 中吗?

c++ - 为什么C++线程/future开销这么大

javascript - 如何暂时用新的 rl.qu/estion 覆盖之前的 rl.qu/estion?

单行中的 Swift 两个 readLine/scanf/stdin 输入

ios - 如何知道用户没有触摸键盘?