Ios Swift:同步Web服务调用

Ios Swift:同步Web服务调用,ios,swift,Ios,Swift,在Swift中,我正在呼叫一家网络服务公司(GooglePlaces),并成功获取GooglePlaces ID 当我遍历JSON响应并获取GooglePlaceID时,我想调用另一个Web服务(GooglePlaceDetails) 使用下面的代码,我得到的响应是: estPlace_ID_1 Return Number Is: Nothing estPlace_ID Return Number Is: Nothing ..... Function Phone Number is: 867-5

在Swift中,我正在呼叫一家网络服务公司(GooglePlaces),并成功获取GooglePlaces ID

当我遍历JSON响应并获取GooglePlaceID时,我想调用另一个Web服务(GooglePlaceDetails)

使用下面的代码,我得到的响应是:

estPlace_ID_1
Return Number Is: Nothing
estPlace_ID
Return Number Is: Nothing
.....
Function Phone Number is: 867-5309
Function Phone Number is: 867-5309
似乎直到for result in results循环完成后,才会执行函数get Details

如何更改代码,使其在继续迭代之前等待执行getDetails

class func getDetails(id: String) -> String {
    <Setup the request>
    let session = NSURLSession.sharedSession()

    //Second Request
    let task = session.dataTaskWithRequest(request) { data, response, error in 
        do {
            //Parse Result
            print("Function Phone Number is" + phoneNumber)
        }
        catch {
        }
    }
    task.resume()

    return phoneNumber
}

//First request
<Setup the request>
let task = session.dataTaskWithRequest(request) { data, response, error in
    //a few checks with guard statements

    do {        
        //Extract results from JSON response
        results = <FROM_JSON>

        for result in results {
            estPlace_ID = result["value"]

            print(estPlace_ID)
            print("return number is" + getDetails(estPlace_ID))              
        }
        catch {       
        }
    }
    task.resume()
}
class-func-getDetails(id:String)->String{
let session=NSURLSession.sharedSession()
//第二个请求
让task=session.dataTaskWithRequest(请求){数据,响应,错误
做{
//解析结果
打印(“功能电话号码为”+电话号码)
}
抓住{
}
}
task.resume()
返回电话号码
}
//第一个请求
让task=session.dataTaskWithRequest(请求){数据,响应,错误
//几张保安声明的支票
做{
//从JSON响应中提取结果
结果=
为了得到结果{
estPlace_ID=结果[“值”]
打印(ESTU ID)
打印(“返回编号为”+getDetails(estPlace_ID))
}
捕获{
}
}
task.resume()
}

在异步调用的结果到达之前进行函数调用阻塞可以通过调度信号量实现。模式是:

create_semaphore()
someAyncCall() {
    signal_semaphore()
}
wait_for_semaphore()
rest_of_the_code()
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)
        }
    }
}
在您的情况下,您可以修改
getDetails
方法,如下所示:

class func getDetails(id: String) -> String {
    <Setup the request>
    let session = NSURLSession.sharedSession()
    let sem = dispatch_semaphore_create(0)

    //Second Request
    let task = session.dataTaskWithRequest(request) { data, response, error in 
        do {
            //Parse Result
            print("Function Phone Number is" + phoneNumber)

        } catch {
        }
        // the task has completed, signal this
        dispatch_semaphore_signal(sem)
    }
    task.resume()

    // wait until the semaphore is signaled
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

    // we won't get here until dispatch_semaphore_signal() is called
    return phoneNumber
}
注意,在上面的示例中,
dispatch\u semaphore\u wait
的第二个参数是
dispatch\u TIME\u FOREVER
,这意味着调用代码将无限期地等待异步调用完成。如果要设置某个超时,可以创建一个
dispatch\u time\t
值并传递它:

// want to wait at most 30 seconds
let timeout = 30
let dispatchTimeout = dispatch_time(DISPATCH_TIME_NOW, timeout * Int64(NSEC_PER_SEC))
dispatch_semaphore_wait(sem, dispatchTimeout)

我建议您采用异步模式。例如,有一个异步检索电话号码的方法,使用完成处理程序报告成功或失败:

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
将同时发出这些请求(出于性能原因)。如果您想限制这一点,可以使用信号量来实现(只需确保在后台队列而不是会话队列上实现这一点)。基本模式是:

create_semaphore()
someAyncCall() {
    signal_semaphore()
}
wait_for_semaphore()
rest_of_the_code()
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
来约束并发性,但这似乎超出了这个问题的范围。但您也可以像上面那样使用信号量来约束并发性。但底线是,如果您遵循异步模式,您将获得最佳的用户体验和性能,而不是试图找出如何使请求同步运行。

您不能从任务本身调用
task.resume()
;“我想他回答中有一个打字错误;提交了一个“编辑以移动”任务。简历如果按顺序这样做,您将付出巨大的性能损失。最好允许并发请求,并在所有请求完成时使用dispatch group通知您自己。或者,更激进地说,通过更显著的性能改进,重构您的web服务,允许您向其发送一个包含多个id号码的请求,您需要这些号码的电话号码。感谢@Rob指出这一点,为了避免死锁,我已更新了答案。谢谢您的回复;我可能会使用调度组(而不是信号量),但在您的回答中无法这样做。你指出了引导我去调度组的信号灯。谢谢你,谢谢你的回答;成功了。我使用了一个稍微不同的实现,但如果没有您的帮助,这是不可能的。谢谢