尝试在完成块内改变数组(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();
}
});
}
}
我的核心数据数据库中存储了城市名称和坐标,并试图获取状态。 为了做到这一点,我像这样进行反向地理编码
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();
}
});
}
}