GCD 信号量不等待 (Swift)
GCD Semaphore does not wait (Swift)
我对 GCD 还很陌生。我有这个用于正向地理编码的函数,问题是它在完成闭包完成之前 returns。所以每次它只是 returns nil。我发现我可以使用信号量,所以 return 等待完成闭包完成,但是网上的例子很少,我发现 none 的一个函数 returns。我试图实现它,但功能仍然 returns 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
}
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.
还有一点,你必须在你的块geocodeAddressString
结束执行之前调用dispatch_semaphore_signal
。否则,如果出现错误,您的应用程序将永远等待。
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
})
最后,当使用永远等待的信号量时,您必须确保信号量内的代码块将结束执行。
正如 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
}
}
我对 GCD 还很陌生。我有这个用于正向地理编码的函数,问题是它在完成闭包完成之前 returns。所以每次它只是 returns nil。我发现我可以使用信号量,所以 return 等待完成闭包完成,但是网上的例子很少,我发现 none 的一个函数 returns。我试图实现它,但功能仍然 returns 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
}
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.
还有一点,你必须在你的块geocodeAddressString
结束执行之前调用dispatch_semaphore_signal
。否则,如果出现错误,您的应用程序将永远等待。
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
})
最后,当使用永远等待的信号量时,您必须确保信号量内的代码块将结束执行。
正如 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
}
}