我建议你采用异步模式。例如,有一个检索电话号码异步,报告与完成处理成功或失败的方法:
let session = NSURLSession.sharedSession()
func requestPhoneNumber(id: String, completionHandler: (String?) -> Void) {
let request = ...
let task = session.dataTaskWithRequest(request) { data, response, error in
do {
let phoneNumber = ...
completionHandler(phoneNumber)
}
catch {
completionHandler(nil)
}
}
task.resume()
}
那么你的第一个请求,检索所有的地方,都将使用这个异步requestDetails
:
// I don't know what your place structure would look like, but let's imagine an `id`,
// some `info`, and a `phoneNumber` (that we'll retrieve asynchronously).
struct Place {
var id: String
var placeInfo: String
var phoneNumber: String?
init(id: String, placeInfo: String) {
self.id = id
self.placeInfo = placeInfo
}
}
func retrievePlaces(completionHandler: ([Place]?) -> Void) {
let request = ...
let task = session.dataTaskWithRequest(request) { data, response, error in
// your guard statements
do {
// Extract results from JSON response (without `phoneNumber`, though
var places: [Place] = ...
let group = dispatch_group_create()
// now let's iterate through, asynchronously updating phone numbers
for (index, place) in places.enumerate() {
dispatch_group_enter(group)
self.requestPhoneNumber(place.id) { phone in
if let phone = phone {
dispatch_async(dispatch_get_main_queue()) {
places[index].phoneNumber = phone
}
}
dispatch_group_leave(group)
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
completionHandler(places)
}
}
}
task.resume()
}
这也采用异步模式,这次使用调度组来确定请求何时完成。
retrievePlaces { phoneNumberDictionary in
guard phoneNumberDictionary != nil else { ... }
// update your model/UI here
}
// but not here
注意,retrievePlaces
会发出同时相对于彼此(由于性能原因)这些请求:当你把这个你应该使用完成处理模式。如果你想限制这个,你可以使用一个信号来做到这一点(只要确保在后台队列上,而不是在会话队列上)。其基本模式是:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
let semaphore = dispatch_semaphore_create(4) // set this to however many you want to run concurrently
for request in requests {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
performAsynchronousRequest(...) {
dispatch_semaphore_signal(semaphore)
}
}
}
所以这可能看起来像:
func retrievePlaces(completionHandler: ([Place]?) -> Void) {
let request = ...
let task = session.dataTaskWithRequest(request) { data, response, error in
// your guard statements
do {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
// Extract results from JSON response
var places: [Place] = ...
let semaphore = dispatch_semaphore_create(4) // use whatever limit you want here; this does max four requests at a time
let group = dispatch_group_create()
for (index, place) in places.enumerate() {
dispatch_group_enter(group)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
self.requestPhoneNumber(place.id) { phone in
if let phone = phone {
dispatch_async(dispatch_get_main_queue()) {
places[index].phoneNumber = phone
}
}
dispatch_semaphore_signal(semaphore)
dispatch_group_leave(group)
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
completionHandler(places)
}
}
}
}
task.resume()
}
坦率地说,当它的这个复杂的,我会经常使用异步NSOperation
子类,并使用maxConcurrentOperationCount
队列的约束并发,但似乎这超出了这个问题的范围。但是你也可以像上面那样使用信号量来约束并发。但底线是,不是试图弄清楚如何使请求同步运行,如果你遵循异步模式,你将获得最好的UX和性能。
你不能在任务本身内调用'task.resume()';那不应该编译。 – jtbandes
@jtbandes我认为他的答案有一个错字;提交了一个编辑来移动task.resume –
你会付出巨大的性能损失来按顺序执行它们。允许并发请求并使用调度组在完成后通知自己会更好。或者,更根本地说,通过更显着的性能改进,重构您的Web服务,以允许您向其发送一个您需要电话号码的多个身份号码的请求。 – Rob