【问题标题】:For Loop with Google Geocoding - High Error Rate - iPhone使用 Google 地理编码的 For 循环 - 高错误率 - iPhone
【发布时间】:2011-11-29 20:37:42
【问题描述】:

我正在使用以下代码通过地理编码获取位置信息,然后将地图图钉添加到 Google 地图视图。该代码使用 For 循环循环遍历我数据库中的每个位置。问题是代码在运行时无法返回大约 50% 的位置信息。这些失败的项目按照下面的代码保存在 failedLoad 数组中。

谁能说明这是为什么?此外,由于这些失败的项目保存在“failedLoad”数组中,有没有办法可以使用这个数组来加载任何丢失的引脚?

编辑

失败的项目是由于 620 错​​误,这意味着我提交项目太快了。如何在代码中添加延迟?

谢谢!

-(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);
       }];

 }


}

【问题讨论】:

  • Google API 有各种查询速率限制,例如每秒最多 10 个请求。如果您打算使用 API,那么您真的应该对所有这些进行 RTFM。
  • 当位置加载失败时,为什么不记录返回的字符串。失败的原因将立即显而易见。
  • 谢谢 - 这是一个 620 错​​误,我知道这是由于我提交的速率所致。我将如何延迟代码?
  • 请注意,这个问题是the followup of this one(但这不是重复的,因为目前的问题是关于 Google API 速率限制和添加请求之间的延迟 - 刚刚提到对于那些对我们如何获得该代码感兴趣的人来说,之前的这篇文章)
  • 如果我记得很清楚,Google Maps Web 服务允许在一次调用中聚合多个请求(我可能是错的)。看看code.google.com/apis/maps/documentation/webservices/index.html

标签: iphone objective-c xcode mkmapview geocoding


【解决方案1】:

不要使用 for 循环同时发送所有请求,您可能应该一个接一个地发送它们(或 5 x 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
}

当然,完成后不要忘记将属性设置回nil(或在dealloc中)以释放内存。

在情况 (1) 中,我在完成块和错误块中都调用了performSelector:withObject:afterDelay,因此只有在第一个请求/解码过程完成后才会调用下一个请求/解码过程。这样你的请求就被序列化了。

在情况 (2)(注释/禁用)中,我在 startRequestWithCompletion:... 方法之后立即调用 performSelector:withObject:afterDelay,因此它不会等待第一个请求结束来弹出下一个请求。但是您将等待(希望)足够长的时间,以免达到 GoogleAPI 速率限制


请注意,这不是唯一的解决方案,还有很多其他可能性。一种是使用NSOperationQueue 在队列中一个接一个地排队请求并添加依赖项,或者在 GCD 串行队列上安排请求的发送过程(是的,我知道我告诉过你不要使用 GCD实际上发送您的请求,但这仍然适用,仍然不使用 GCD+同步请求,但是您可以使用 GCD 对将在主线程上一个接一个地调用 [OHURLLoader startRequestWithCompletion:...] 的块进行排队;请求本身仍由 RunLoop 在主线程上执行)

【讨论】:

  • 谢谢 - 这似乎运作良好。但是,应用程序在完成添加引脚后会崩溃。 mapLocationsToGeocode 数组为空后,如何停止选择器/计时器?
  • 确定修复:[self performSelector:@selector(popLocationAndGeocode) withObject:nil afterDelay:0.15]; int x = [mapLocationsToGeocode 计数]; if (x
  • 哦,是的,忘记了停止条件;)不要安排“performSelector”请求然后立即取消,您根本不应该安排它,即使用if ([mapLocationsToGeocode count]) [self performSelector:... withObject:nil afterDelay:...]
  • 如果您选择解决方案 (2) 而不是 (1)(并取消注释该行),也要小心,我刚刚发现了一个问题:您可能会尝试弹出相同的 PlaceObject 两次,如果对popLocationAndGeocode(和[mapLocationsToGeocode anyObject] 调用)的下一次调用在待处理请求完成之前执行(即在到达第一个请求的[mapLocationsToGeocode removeObject:onePlace] 调用之前)。您可以在调用 anyObject 后立即从集合中移除该对象(但在这种情况下,不要忘记在将其从集合中移除之前保留它,以避免它被过早释放!)
  • 编辑了我的代码,希望能解决我在上一条评论中谈到的问题
猜你喜欢
  • 2011-11-29
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 2016-01-20
  • 1970-01-01
  • 2012-01-17
  • 2019-11-06
  • 1970-01-01
相关资源
最近更新 更多