2016-06-29 3 views
0

Я очень много нового для GCD. У меня есть эта функция для прямого геокодирования, и проблема в том, что она возвращается до завершения завершения завершения. Поэтому каждый раз, когда он просто возвращает , ноль. Я узнал, что могу использовать семафоры, чтобы возврат ожидал завершения закрытия, но примеров в Интернете мало, и я не нашел ни одной из возвращаемых функций. Я попытался реализовать его, но функция по-прежнему возвращает nil, хотя местоположение распечатывается на консоль через несколько секунд. Если бы кто-то мог сказать мне, где я ошибаюсь, я был бы очень благодарен.GCD Semaphore не ждет (Swift)

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 
} 
+1

Вы собираетесь об этом неправильном пути. Вы должны передать закрытие 'forwardGeocoding' и вызывать это из обработчика завершения, а не пытаться вернуть значение. Ваш текущий подход может блокировать основную очередь, особенно если вы не сигнализируете о семафоре во всех случаях в обработчике завершения. – Paulw11

ответ

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 
    } 
} 
+0

Работает как шарм! Спасибо за совет, мне определенно нужно изучить блоки завершения. Еще раз большое спасибо. – MHCsk

+0

Если вы не возражаете, не могли бы вы объяснить мне, что именно делает «если допустимые метки - метки, где? Placemarks.isEmpty»? Это меня смущает, и я бы с удовольствием знал :) – MHCsk

+0

Нет проблем, 'if let placemarks = placemarks' распаковывает необязательный, если это не' nil', а предложение 'where' проверяет, пуст ли массив. Условие 'if' истинно, если' placemarks' не 'nil', а не' empty'. 'isEmpty' является синонимом' .count == 0' – vadian

0

результат отправки_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 
    }) 

Наконец, при использовании семафора, что ждать навсегда, вы должны убедиться, что блок кода внутри семафора закончит выполнение.

Смежные вопросы