блокировать, пока reverseGeocode не вернется

Я пытаюсь найти местоположение пользователя по координате для сохранения в моей базе данных.

Чтобы найти название местоположения, я использую reverseGeocode. Однако, поскольку это блочный метод, мой self.locationName вернется (и сохранится как nil) в базу данных. Итак, я попытался найти решение проблемы и попытался собрать следующее решение, используя семафоры, чтобы попытаться заблокировать, пока я не получу имя местоположения, которое я могу сохранить, но приложение просто зависает при нажатии кнопки сохранения. Должен ли я даже решать эту проблему таким образом или есть лучший способ?

 dispatch_semaphore_t semaphore;

 - (void)reverseGeocode:(CLLocation *)location {
     CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks,                    NSError *error) {

     NSLog(@"Finding address");
     if (error) {
     NSLog(@"Error %@", error.description);
     } else {
     CLPlacemark *placemark = [placemarks lastObject];
     self.locationName = [NSString stringWithFormat:@"%@", ABCreateStringWithAddressDictionary(placemark.addressDictionary, NO)];
     dispatch_semaphore_signal(semaphore);         
            }
     }];
     }



-(NSString *)findLocation:(CLLocation *)startingLocation
{
semaphore = dispatch_semaphore_create(0);
[self reverseGeocode:startingLocation];
 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); //should maybe timeout
return self.locationName;
}

person Sarah92    schedule 27.02.2014    source источник
comment
Переработайте свой код так, чтобы вместо findLocation: возвращаемой строки он вместо этого принимал блок, который вы затем можете вызвать после завершения метода обратного геокодирования. Этот блок будет обрабатывать сохранение имени местоположения в вашей базе данных.   -  person Gavin    schedule 28.02.2014
comment
Чтобы расширить ответ Гэвина, почти никогда не бывает правильным подход к вызову синхронной сети (или другого длительного процесса).   -  person David Berry    schedule 28.02.2014
comment
Спасибо за предложение @Gavin. Я знал, что пытаюсь найти обходной путь, когда начал думать о семафорах!   -  person Sarah92    schedule 28.02.2014


Ответы (1)


Вы думаете обо всем этом неправильно. Это не то, как работает асинхронный код. Не блокируйте НЕ, пока код не вернется. Просто запустите код, чтобы начать обратное геокодирование, и закончите. Теперь, когда обратное геокодирование завершается, оно перезванивает вам, и вы можете делать с возвращенной информацией все, что хотите. В этом весь смысл обработчика завершения: он не запускается, пока не завершится обратное геокодирование.

Просто избавьтесь от семафоров и позвольте событиям происходить асинхронно. Вот полный пример без вторичного метода:

CLLocation* loc = userLocation.location;
[geo reverseGeocodeLocation:loc
          completionHandler:^(NSArray *placemarks, NSError *error)
  {
      if (placemarks) {
          CLPlacemark* p = [placemarks objectAtIndex:0];
          NSLog(@"%@", p.addressDictionary); // do something with address
      }
  }];

Как вам уже сказали, если вы действительно хотите вызвать это из другого метода, а затем сделать что-то дальше, то передайте блок этому методу и вызовите блок внутри обработчика завершения. Это означает, что переданный вами блок — это код, который запустится после завершения геокодирования, а это именно то, что вам нужно — без семафоров и без зависания приложения.

Замораживание приложения — плохой тон, и WatchDog убьет ваше приложение, если вы будете делать это слишком долго. Просто не делай этого.

person matt    schedule 27.02.2014
comment
что значит закончить это перезванивает вам обратно?? - person Sarah92; 28.02.2014
comment
закончить значит остановиться. Ваш код заканчивается. Теперь интерфейс не будет зависать. Он перезванивает вам означает, что обработчик завершения в последний раз запускается (возможно, через некоторое время после того, как вы запустили процесс). - person matt; 28.02.2014
comment
Но как я узнаю, что обработчик завершения запущен, прежде чем я попытаюсь сохранить locationName? Этот код также находится в отдельном классе от класса базы данных, из которого я его вызываю. Поэтому я просто хочу убедиться, что у меня есть имя местоположения и я могу его сохранить. Я не хочу ждать «когда-нибудь», чтобы это произошло, прежде чем я продолжу. - person Sarah92; 28.02.2014
comment
Вы не ждете. В этом весь смысл. Вы должны структурировать свою архитектуру так, чтобы не имело значения, когда произойдет завершение. Вот что такое асинхронность. - person matt; 28.02.2014
comment
Вам может помочь мой пример: github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ Мы передаем обработчик завершения, когда вызываем download:completionHander:. Обработчик завершения сохраняется. Позже - может быть, намного позже! - вызывается URLSession:downloadTask:didFinishDownloadingToURL:; в этот момент мы достаем обработчик завершения, который нам дали в начале, и вызываем его! Таким образом, мы перезваниваем клиенту, который изначально звонил download:completionHander:, в полном порядке, когда придет время. - person matt; 28.02.2014