Цикл с Google Geocoding - высокий уровень ошибок - iPhone - PullRequest
1 голос
/ 03 октября 2011

Я использую приведенный ниже код, чтобы получить информацию о местоположении с помощью геокодирования, а затем добавить пин-код карты в Google Map View.Код использует цикл For для циклического прохождения каждого места в моей базе данных.Проблема в том, что код не может вернуть информацию о местоположении примерно для 50% мест при запуске.Эти неудачные элементы сохраняются в массиве failedLoad в соответствии с приведенным ниже кодом.

Кто-нибудь может подсказать, почему это так?Кроме того, поскольку эти элементы с ошибками сохраняются в массиве failedLoad, есть ли способ использовать этот массив для загрузки недостающих выводов?

EDIT

Элементы с ошибками вызваныошибка, которая означает, что я отправляю товары слишком быстро.Как мне добавить задержку в код?

Спасибо!

-(void)displayPlaces {


for (PlaceObject *info in mapLocations) {

        // GET ANNOTATION INFOS
        NSString * addressOne = info.addressOne;
        NSString * name = info.name;
        NSString * postCode = info.postCode;

        NSString * addressTwo = [addressOne stringByAppendingString:@",London,"];
        NSString * address = [addressTwo stringByAppendingString:postCode];

       NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
       NSURL* url = [NSURL URLWithString:urlString];
       NSURLRequest* req = [NSURLRequest requestWithURL:url];

       OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
       [loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
           NSString* locationString = loader.receivedString;
           NSArray *listItems = [locationString componentsSeparatedByString:@","];

        double latitude = 0.0;
        double longitude = 0.0;

        if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
            latitude = [[listItems objectAtIndex:2] doubleValue];
            longitude = [[listItems objectAtIndex:3] doubleValue];

        }

        else {

            NSLog(@"Error %@",name);
            [failedLoad addObject : info];

        }  


        CLLocationCoordinate2D coordinate;
        coordinate.latitude = latitude;
        coordinate.longitude = longitude;
        MyLocation *annotation = [[[MyLocation alloc] initWithName:name address:address coordinate:coordinate] autorelease];

        [mapViewLink addAnnotation:annotation];

       } errorHandler:^(NSError *error) {
           NSLog(@"Error while downloading %@: %@",url,error);
       }];

 }


}

1 Ответ

2 голосов
/ 03 октября 2011

Вместо использования цикла for и отправки всех ваших запросов одновременно, вы, вероятно, должны отправлять их один за другим (или 5 на 5?)

Вот один из способов сделать это (не тестировался в реальном коде, просто набирается, когда я иду, поэтому может иметь опечатку) :

// In the instance variables, have:
@property(retain) NSMutableSet* mapLocationsToGeocode;

// When you want to decode, use:
self.mapLocationsToGeocode = [NSMutableSet setWitharray:mapLocations];
// (Or add to the existing NSSet if you have one and add Places using multple passes)
[self popLocationAndGeocode];

-(void)popLocationAndGeocode
{
  // Pop any location from the set
  PlaceObject* onePlace = [mapLocationsToGeocode anyObject];

  // Build the URL given the PlaceObject
  NSString* address = [NSString stringWithFormat:@"%@,London,%@",info.addressOne,info.postCode];
  NSString* name = info.name;

  NSString* urlString = [NSString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=csv", [address stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
  NSURLRequest* req = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];

  // Remove it so it won't be poped again
  [mapLocationsToGeocode removeObject:onePlace];

  // Send the request here to decode the PlaceObject
  OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
  [loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
    NSString* locationString = loader.receivedString;
    NSArray* listItems = [locationString componentsSeparatedByString:@","];
    ...

    if([listItems count] >= 4 && [[listItems objectAtIndex:0] isEqualToString:@"200"]) {
        // Process with latitude and longitude, add your MKAnnotation, etc
    } else {
        NSLog(@"Error %@",name);
        [failedPlaces addObject:onePlace];
    }  
    ...

     // Schedule the next decoding request (1)
     if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.1];
   } errorHandler:^(NSError *error) {
       NSLog(@"Error while downloading %@: %@",url,error);
       [failedPlaces addObject:onePlace];

       // Schedule the next decoding request anyway (1)
       if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.1];
   }];

   // Schedule the next decoding request (2) -- solution 2
   // if ([mapLocationsToGeocode count]) [self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:1.0]; // wait one sec before sending next request
}

Конечно, не забудьте установить свойство обратно в ноль, когда закончите (или в dealloc), чтобы освободить память.

В случае (1) я вызываю performSelector:withObject:afterDelay как в блоках завершения, так и в блоках ошибок, так что следующий процесс запроса / декодирования вызывается только после завершения первого. Таким образом, ваши запросы несколько сериализуются.

В случае (2) (закомментировано / отключено) я вызываю performSelector:withObject:afterDelay сразу после метода startRequestWithCompletion:..., поэтому он не будет ждать окончания первого запроса, чтобы выскочить следующий. Но вы будете ждать (надеюсь) достаточно долго, чтобы не достичь предела скорости GoogleAPI


Обратите внимание, что это не единственное решение, есть много других возможностей. Одним из них является использование NSOperationQueue для постановки запросов один за другим в очередь и добавления в нее зависимостей или для планирования процесса отправки запросов в последовательную очередь GCD (да, я знаю, что я говорил вам не использовать GCD для на самом деле отправляет ваши запросы, но они все еще применяются, но по-прежнему не используют GCD + синхронные запросы, но вы можете использовать GCD для постановки в очередь блоков, которые будут вызывать [OHURLLoader startRequestWithCompletion:...] в главном потоке один за другим, сам запрос все еще выполняется в основном потоке с помощью RunLoop)

...