GCD Semaphore不等待(Swift)

时间:2016-06-29 10:12:58

标签: ios swift grand-central-dispatch

我对GCD很陌生。我有这个函数用于前向地理编码,问题是它在完成闭包完成之前返回。所以每次它只返回 nil 。我发现我可以使用信号量,所以返回等待完成闭包完成,但是在网上有很少的例子,我发现没有返回的函数。我尝试实现它,但函数仍然返回 nil ,即使稍后将该位置打印到控制台。如果有人能告诉我我在哪里犯了错误,我将非常感激。

func forwardGeocoding(address: String) -> CLLocation? {
    var userLocation: CLLocation?
    var returnvalue: CLLocation?
    let semaphore = dispatch_semaphore_create(0)

    CLGeocoder().geocodeAddressString(address, completionHandler: { (placemarks, error) in
        if error != nil {
            print("Geocoding error: \(error)")
            return
        }
        if placemarks?.count > 0 {
            let placemark = placemarks?.first
            let location = placemark?.location
            let coordinate = location?.coordinate
            print("Settings location: \(coordinate!.latitude), \(coordinate!.longitude)")
            if let unwrappedCoordinate = coordinate {
                let CLReadyLocation: CLLocation = CLLocation(latitude: unwrappedCoordinate.latitude, longitude: unwrappedCoordinate.longitude)
                userLocation = CLReadyLocation
                dispatch_semaphore_signal(semaphore)
            }
        }
    })

    let wait = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

    if wait != 0 {
        returnvalue = userLocation
    }
    return returnvalue
}

2 个答案:

答案 0 :(得分:1)

正如Paulw11已经提到的,在这种情况下,信号量是非常糟糕的编程习惯。如果您是GCD的新手,请学习如何理解在完成块中返回接收数据的异步模式。在Swift中处理比在Objective-C中更简单。

这是使用完成块的示例:

func forwardGeocoding(address: String, completion: (CLLocation?, NSError?) -> Void) {

  CLGeocoder().geocodeAddressString(address, completionHandler: { (placemarks, error) in
    if error != nil {

      completion(nil, error!)
    } else {
      if let placemarks = placemarks where !placemarks.isEmpty {
        let placemark = placemarks.first!
        if let unwrappedLocation = placemark.location {
          let coordinate = unwrappedLocation.coordinate
          print("Settings location: \(coordinate.latitude), \(coordinate.longitude)")
          let CLReadyLocation = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
          completion(CLReadyLocation, nil)
        }
      }
    }
  })
}

并用

调用它
forwardGeocoding("foo") { (location, error) in
  if error != nil {
    print("Geocoding error: \(error!)")
  } else {
    // do something with the location
  }
}

答案 1 :(得分:0)

dispatch_semaphore_wait的结果在成功时为0,在超时时为非零。因此,您应该将代码更改为: let wait = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

if wait == 0 { //your work is done, without time out.
    returnvalue = userLocation //update location
}
return returnvalue //otherwise return nil according to your code above. this code will never execute. In this case, there is no time out cause it wait forever.

另一点,您必须在阻止dispatch_semaphore_signal结束执行之前调用geocodeAddressString。否则,如果出现错误,您的应用程序将永远等待。

CLGeocoder().geocodeAddressString(address, completionHandler: { (placemarks, error) in
        if error != nil {
            print("Geocoding error: \(error)")
            dispatch_semaphore_signal(semaphore) //add to here
            return
        }
        if placemarks?.count > 0 {
            let placemark = placemarks?.first
            let location = placemark?.location
            let coordinate = location?.coordinate
            print("Settings location: \(coordinate!.latitude), \(coordinate!.longitude)")
            if let unwrappedCoordinate = coordinate {
                let CLReadyLocation: CLLocation = CLLocation(latitude: unwrappedCoordinate.latitude, longitude: unwrappedCoordinate.longitude)
                userLocation = CLReadyLocation
            }
        }
        dispatch_semaphore_signal(semaphore) //and here
    })

最后,当使用永远等待的信号量时,你必须确保信号量内的代码块将结束执行。