Swift CLGeocoder()意外返回nil
我有一个位置列表(大约30个元素): 我的目的是从该列表中获取街道名称,因此我决定使用Swift CLGeocoder()意外返回nil,swift,cllocation,clgeocoder,Swift,Cllocation,Clgeocoder,我有一个位置列表(大约30个元素): 我的目的是从该列表中获取街道名称,因此我决定使用CLGeocoder()。 我在viewDidLoad()中调用一个函数,每个位置都由lookUpCurrentLocation()处理 我的问题是: 当应用程序启动时,它会打印一个nil列表,或者只打印前两个nil和其他街道名称 我希望看到整个列表在没有任何零的情况下得到处理。 有什么提示吗?正如Leo所说,您不希望同时运行请求。正如上面所说: 启动反向地理编码请求后,不要尝试启动另一个反向或正向地理编码
CLGeocoder()
。
我在viewDidLoad()
中调用一个函数,每个位置都由lookUpCurrentLocation()
处理
我的问题是:
当应用程序启动时,它会打印一个nil列表,或者只打印前两个nil和其他街道名称
我希望看到整个列表在没有任何零的情况下得到处理。
有什么提示吗?正如Leo所说,您不希望同时运行请求。正如上面所说: 启动反向地理编码请求后,不要尝试启动另一个反向或正向地理编码请求。每个应用程序的地理编码请求都有速率限制,因此在短时间内发出过多请求可能会导致某些请求失败。当超过最大速率时,地理编码器会将带有该值的错误对象传递给完成处理程序 有几种方法可以使这些异步请求按顺序运行:
func retrievePlacemarks(at index: Int = 0) {
guard index < locations.count else { return }
lookUpCurrentLocation(location: locations[index]) { name in
print(name ?? "no name found")
DispatchQueue.main.async {
self.retrievePlacemarks(at: index + 1)
}
}
}
FWIW,在进行地理编码时,我可能首先使用而不是[0]
:
func lookUpCurrentLocation(location: CLLocation, completionHandler: @escaping (String?) -> Void) {
CLGeocoder().reverseGeocodeLocation(location) { placemarks, _ in
completionHandler(placemarks?.first?.name)
}
}
extension CLGeocoder {
func reverseGeocodeLocationPublisher(_ location: CLLocation, preferredLocale locale: Locale? = nil) -> AnyPublisher<CLPlacemark, Error> {
Future<CLPlacemark, Error> { promise in
self.reverseGeocodeLocation(location, preferredLocale: locale) { placemarks, error in
guard let placemark = placemarks?.first else {
return promise(.failure(error ?? CLError(.geocodeFoundNoResult)))
}
return promise(.success(placemark))
}
}.eraseToAnyPublisher()
}
}
我不认为reverseGeocodeLocation
可以返回一个非nil
的零长度数组(在这种情况下,您的格式副本将因无效的下标错误而崩溃),但上述操作与您的操作完全相同,但也消除了潜在的错误
操作
子类中(如中的通用异步操作
)
然后,您可以定义反向地理编码操作:
class ReverseGeocodeOperation: AsynchronousOperation {
private static let geocoder = CLGeocoder()
let location: CLLocation
private var geocodeCompletionBlock: ((String?) -> Void)?
init(location: CLLocation, geocodeCompletionBlock: @escaping (String?) -> Void) {
self.location = location
self.geocodeCompletionBlock = geocodeCompletionBlock
}
override func main() {
ReverseGeocodeOperation.geocoder.reverseGeocodeLocation(location) { placemarks, _ in
self.geocodeCompletionBlock?(placemarks?.first?.name)
self.geocodeCompletionBlock = nil
self.finish()
}
}
}
然后,您可以创建串行操作队列,并将反向地理编码操作添加到该队列:
private let geocoderQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = Bundle.main.bundleIdentifier! + ".geocoder"
queue.maxConcurrentOperationCount = 1
return queue
}()
func retrievePlacemarks() {
for location in locations {
geocoderQueue.addOperation(ReverseGeocodeOperation(location: location) { string in
print(string ?? "no name found")
})
}
}
func lookUpCurrentLocation(location: CLLocation, completionHandler: @escaping (String?) -> Void) {
CLGeocoder().reverseGeocodeLocation(location) { placemarks, _ in
completionHandler(placemarks?.first?.name)
}
}
extension CLGeocoder {
func reverseGeocodeLocationPublisher(_ location: CLLocation, preferredLocale locale: Locale? = nil) -> AnyPublisher<CLPlacemark, Error> {
Future<CLPlacemark, Error> { promise in
self.reverseGeocodeLocation(location, preferredLocale: locale) { placemarks, error in
guard let placemark = placemarks?.first else {
return promise(.failure(error ?? CLError(.geocodeFoundNoResult)))
}
return promise(.success(placemark))
}
}.eraseToAnyPublisher()
}
}
无可否认,还有其他方法可以让异步任务按顺序运行(通常涉及使用信号量或调度组调用
wait
),但我认为这些模式不可取,因此我将它们从上面的备选方案列表中排除。正如Leo所说,您不希望同时运行这些请求。正如上面所说:
启动反向地理编码请求后,不要尝试启动另一个反向或正向地理编码请求。每个应用程序的地理编码请求都有速率限制,因此在短时间内发出过多请求可能会导致某些请求失败。当超过最大速率时,地理编码器会将带有该值的错误对象传递给完成处理程序
有几种方法可以使这些异步请求按顺序运行:
func retrievePlacemarks(at index: Int = 0) {
guard index < locations.count else { return }
lookUpCurrentLocation(location: locations[index]) { name in
print(name ?? "no name found")
DispatchQueue.main.async {
self.retrievePlacemarks(at: index + 1)
}
}
}
FWIW,在进行地理编码时,我可能首先使用而不是[0]
:
func lookUpCurrentLocation(location: CLLocation, completionHandler: @escaping (String?) -> Void) {
CLGeocoder().reverseGeocodeLocation(location) { placemarks, _ in
completionHandler(placemarks?.first?.name)
}
}
extension CLGeocoder {
func reverseGeocodeLocationPublisher(_ location: CLLocation, preferredLocale locale: Locale? = nil) -> AnyPublisher<CLPlacemark, Error> {
Future<CLPlacemark, Error> { promise in
self.reverseGeocodeLocation(location, preferredLocale: locale) { placemarks, error in
guard let placemark = placemarks?.first else {
return promise(.failure(error ?? CLError(.geocodeFoundNoResult)))
}
return promise(.success(placemark))
}
}.eraseToAnyPublisher()
}
}
我不认为reverseGeocodeLocation
可以返回一个非nil
的零长度数组(在这种情况下,您的格式副本将因无效的下标错误而崩溃),但上述操作与您的操作完全相同,但也消除了潜在的错误
操作
子类中(如中的通用异步操作
)
然后,您可以定义反向地理编码操作:
class ReverseGeocodeOperation: AsynchronousOperation {
private static let geocoder = CLGeocoder()
let location: CLLocation
private var geocodeCompletionBlock: ((String?) -> Void)?
init(location: CLLocation, geocodeCompletionBlock: @escaping (String?) -> Void) {
self.location = location
self.geocodeCompletionBlock = geocodeCompletionBlock
}
override func main() {
ReverseGeocodeOperation.geocoder.reverseGeocodeLocation(location) { placemarks, _ in
self.geocodeCompletionBlock?(placemarks?.first?.name)
self.geocodeCompletionBlock = nil
self.finish()
}
}
}
然后,您可以创建串行操作队列,并将反向地理编码操作添加到该队列:
private let geocoderQueue: OperationQueue = {
let queue = OperationQueue()
queue.name = Bundle.main.bundleIdentifier! + ".geocoder"
queue.maxConcurrentOperationCount = 1
return queue
}()
func retrievePlacemarks() {
for location in locations {
geocoderQueue.addOperation(ReverseGeocodeOperation(location: location) { string in
print(string ?? "no name found")
})
}
}
func lookUpCurrentLocation(location: CLLocation, completionHandler: @escaping (String?) -> Void) {
CLGeocoder().reverseGeocodeLocation(location) { placemarks, _ in
completionHandler(placemarks?.first?.name)
}
}
extension CLGeocoder {
func reverseGeocodeLocationPublisher(_ location: CLLocation, preferredLocale locale: Locale? = nil) -> AnyPublisher<CLPlacemark, Error> {
Future<CLPlacemark, Error> { promise in
self.reverseGeocodeLocation(location, preferredLocale: locale) { placemarks, error in
guard let placemark = placemarks?.first else {
return promise(.failure(error ?? CLError(.geocodeFoundNoResult)))
}
return promise(.success(placemark))
}
}.eraseToAnyPublisher()
}
}
无可否认,还有其他方法可以使异步任务按顺序运行(通常涉及使用信号量或调度组调用
wait
),但我认为这些模式不可取,因此我将它们从上面的备选方案列表中排除。下面是一个使用Combine的实现,使用持久缓存。需要更多智能缓存到期逻辑等,但这是一个起点。欢迎光临
下面是一个使用Combine的实现,带有持久缓存。需要更多智能缓存到期逻辑等,但这是一个起点。欢迎光临
尝试打印错误
打印(错误)
启动反向地理编码请求后,不要尝试启动另一个反向或正向地理编码请求。每个应用程序的地理编码请求都有速率限制,因此在短时间内发出过多请求可能会导致某些请求失败。当超过最大速率时,地理编码器会将一个值为cleror.Code.network的错误对象传递给您的完成处理程序。@LeoDabus谢谢,它会打印我:error Domain=kclerordomain Code=2(null)Code 2 iscleror.Code.network.rawValue
。请尝试打印您的错误print(error)
启动反向地理编码请求后,不要尝试启动另一个反向或正向地理编码请求。每个应用程序的地理编码请求都有速率限制,因此在短时间内发出过多请求可能会导致某些请求失败。当超过最大速率时,地理编码器会将一个值为cleror.Code.network的错误对象传递给您的完成处理程序。@LeoDabus谢谢,它会打印我:error Domain=kclerordomain Code=2(null)Code 2 iscleror.Code.network.rawValue
。谢谢!写这篇文章伤了我的脑筋,但它实际上只是上面Rob方法的一个实现。我刚刚更新了它,结果发现你想缓存placemark本身,而不是一个公正的发布者。我还添加了一个惯用的PlacemarkReader