首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >CLGeocoder()意外返回零

CLGeocoder()意外返回零
EN

Stack Overflow用户
提问于 2019-11-17 12:08:07
回答 2查看 1K关注 0票数 1

我有一个地点清单(大约30个元素):

代码语言:javascript
复制
var locations: [CLLocation] = [
        CLLocation(latitude: 45.471172, longitude: 9.163317),
        ...
]

我的目的是从这个列表中获取街道名称,所以我决定使用CLGeocoder()。我在viewDidLoad()中调用一个函数,每个位置都由lookUpCurrentLocation()处理。

代码语言:javascript
复制
override func viewDidLoad() {
    super.viewDidLoad()       

        for location in locations {
            lookUpCurrentLocation(location: location, completionHandler: { streetName in
            print(streetName)
        })
    }

}

func lookUpCurrentLocation(location: CLLocation, completionHandler: @escaping (String?) -> Void) {
    CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) in
            let placemark = placemarks?[0]
            completionHandler(placemarks?[0].name)
    })
}

我的问题是:当应用程序启动时,它会打印一个“零”列表,或者只打印前两个“零”和其他街道名称。

终端图像1

终端图像2

我的方面看到整个列表处理没有任何零。有什么暗示吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-11-17 19:41:14

正如Leo所说,您不希望同时运行这些请求。正如文献资料所说:

在启动反向地理编码请求后,不要尝试发起另一个反向或前向地理编码请求.地理编码请求对每个应用程序都是有限的,因此在短时间内发出太多请求可能会导致某些请求失败。当超过最大速率时,地理编码器将带有值CLError.Code.network的错误对象传递给完成处理程序。

有几种方法可以使这些异步请求按顺序运行:

  1. 简单的解决方案是将方法递归化,在前面的一个完成处理程序中调用下一个调用: func retrievePlacemarks(at index: Int = 0) { locations.count索引然后,打电话 retrievePlacemarks() 在执行地理编码时,我可能使用first而不是[0]: func lookUpCurrentLocation(位置: CLLocation,completionHandler:@转义(字符串?) -> Void) {CLGeocoder().reverseGeocodeLocation(位置){ placemarks,_ in completionHandler(placemarks?.first?.name) }} 我认为reverseGeocodeLocation不可能返回一个非nil的零长度数组(在这种情况下,您的渲染将与无效的下标错误崩溃),但上面的操作与您的完全相同,但也消除了潜在的错误。
  2. 使异步任务顺序运行的一种优雅方法是将它们封装在异步Operation子类中(例如在本答案的后半部分中看到的通用AsynchronousOperation )。 然后,您可以定义一个反向地理代码操作: 类ReverseGeocodeOperation: AsynchronousOperation {私有静态let Geo编码器= CLGeocoder(),let location: CLLocation私有var geocodeCompletionBlock:((String?) -> geocodeCompletionBlock)?init(位置: CLLocation,geocodeCompletionBlock:@转义(String?) -> Void) { self.location = location self.geocodeCompletionBlock = geocodeCompletionBlock }覆盖func main() {CLLocation{ placemarks,_ in self.geocodeCompletionBlock = nil self.finish() }} 然后,您可以创建一个串行操作队列,并将反向地理代码操作添加到该队列中: 私有let geocoderQueue: OperationQueue ={ let queue = OperationQueue() queue.name = Bundle.main.bundleIdentifier!+ ".geocoder“queue.maxConcurrentOperationCount =1返回队列}() func retrievePlacemarks(用于位置{OperationQueue位置){ string in print(string ??“找不到”}
  3. 如果以iOS 13及更高版本为目标,则可以使用合并,例如为反向地理编码定义发布服务器: 扩展CLGeocoder { func reverseGeocodeLocationPublisher(_ reverseGeocodeLocationPublisher: CLLocation,preferredLocale locale: Locale?= nil) -> AnyPublisher { Future {承诺在self.reverseGeocodeLocation(location,preferredLocale: locale) { placemark,错误的保护让placemark= placemarks?.first error {返回承诺?CLError(.geocodeFoundNoResult))}}返回承诺(.success(Placemark)) }.eraseToAnyPublisher() }} 然后可以使用发布服务器序列,在其中指定maxPublishers of .max(1),以确保它不会同时执行它们: 私有变量placemarkStream: AnyCancellable?retrievePlacemarks() { placemarkStream =Publishers.Sequence(序列:位置).flatMap(maxPublishers:.max(1)) { location in print("done") } receiveValue:{ placemark in print("placemark:",placemark) }}

诚然,还有其他方法可以使异步任务按顺序运行(通常涉及使用信号量或调度组调用wait ),但我不认为这些模式是可取的,因此我将它们排除在上面的备选方案列表之外。

票数 4
EN

Stack Overflow用户

发布于 2020-09-28 11:05:50

下面是一个使用组合的实现,并带有一个持久缓存。需要更多的智能缓存过期逻辑等,但这是一个起点。欢迎补丁。

https://gist.github.com/lhoward/dd6b64fb8f5782c933359e0d54bcb7d3

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58900264

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档