尝试在完成块内改变数组(CLGeocoder 反向地理编码)

Trying to mutate array within completion block (CLGeocoder reverse geocoding)

我的核心数据数据库中存储了城市名称和坐标,并试图获取状态。 为了做到这一点,我像这样进行反向地理编码

code moved below 

问题是 locationStr 对象没有存储在 tempCities 数组中。我已经在块内进行了测试,我知道 locationStr 正在创建并存在。

几个小时以来,我一直在努力解决这个问题。有人可以帮我解决这个问题吗?

如果您需要任何其他信息,请告诉我。

编辑:

数组正用于填充 table 视图。该代码位于一个辅助方法中,该方法 returns 一个数组(tempCities 数组)。我在 for 循环之后立即检查数组是否为 nil 和 0。

下面是 UISearchControllerDelegate 方法在视图控制器中的样子

- (void)updateSearchResultsForSearchController:(UISearchController *)searchController
{
NSString *searchText = searchController.searchBar.text;
if ([searchText length] >= 3)
    {
    self.resultsArray = [[[CityHelper sharedInstance]testWithSearch:searchText]mutableCopy];
    [self.resultsTableView reloadData];
    }
}        

并且在 CityHelper class

- (NSArray *) testWithSearch: (NSString *)search
{
NSArray *cities = [self getCitiesStartingWith:search];//stores NSManagedObject subclass instances with cityName, lat, and long.
NSArray *coords = [self coordinatesForCities:cities];

NSMutableArray *tempCities = [NSMutableArray new];

for (MMCoordinate *coordinate in coords) {
    CLGeocoder *geocoder = [CLGeocoder new];
    CLLocation *location = [[CLLocation alloc]initWithLatitude:[coordinate.latitude floatValue]
                                                     longitude:[coordinate.longitude floatValue]];
    if (![geocoder isGeocoding]) {
        [geocoder reverseGeocodeLocation:location completionHandler:^(NSArray *placemarks, NSError *error) {
            CLPlacemark *placemark = placemarks[0];

            NSString *locationStr = [NSString stringWithFormat:@"%@, %@", placemark.locality, placemark.administrativeArea];
            [tempCities addObject:locationStr];
        }];
    }
}
if (!tempCities) {
    NSLog(@"its nil");
}
if ([tempCities count] == 0) {
    NSLog(@"its 0");
}
return tempCities;
}

这总是 returns 一个空的(0 计数)数组

根本原因是将 tempCities 声明为 __block 变量。由于 __block,iOS 在进入完成块时不保留 tempCities。由于完成块稍后执行并且 tempCities 是局部变量,因此它实际上在完成块开始执行时被释放。

请声明tempCities如下:

NSMutableArray *tempCities = [NSMutableArray new];

基本上,您的情况可以用一个简单的代码片段来说明:

- (void)someMethod
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // this will be executed in two seconds
        NSLog(@"I'm the last!");
    });

    NSLog(@"I'm first!");
}

传递给 dispatch_after 的块在 方法调用结束后 被调用,就像您传递给 reverseGeocodeLocation:completionHandler: 的块一样。您将首先在控制台上看到 I'm first,然后是 I'm the last!

你该怎么办?

您需要通过向您的方法引入回调来解决该问题,因为您的方法在后台执行操作并且需要在完成时回调。例如,当您想知道如何在方法中声明块时,该网站解释了如何使用块语法:http://fuckingblocksyntax.com/

在您的情况下,您还需要在 reverseGeocodeLocation:completionHandler 块中确定何时调用您应添加到 testWithSearch 作为参数的回调。例如,您可以在每次调用 reverseGeocodeLocation:completionHandler 时增加一个计数器,并在每次调用 completionHandler 时减少它。当计数器达到 0 时,您使用数组结果调用 testWithSearch 回调。

示例:

- (void)doSomething:(dispatch_block_t)callback
{
    // we need __block here because we need to
    // modify that variable inside a block
    // try to remove __block and you'll see a compiler error
    __block int counter = 0;

    for (int i = 0; i < 15; i ++) {
        counter += 1;
        dispatch_async(dispatch_get_main_queue(), ^{
            counter -= 1;
            if (counter <= 0 && callback) {
                callback();
            }
        });
    }
}