Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/video/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Swift CLGeocoder()意外返回nil_Swift_Cllocation_Clgeocoder - Fatal编程技术网

Swift CLGeocoder()意外返回nil

Swift CLGeocoder()意外返回nil,swift,cllocation,clgeocoder,Swift,Cllocation,Clgeocoder,我有一个位置列表(大约30个元素): 我的目的是从该列表中获取街道名称,因此我决定使用CLGeocoder()。 我在viewDidLoad()中调用一个函数,每个位置都由lookUpCurrentLocation()处理 我的问题是: 当应用程序启动时,它会打印一个nil列表,或者只打印前两个nil和其他街道名称 我希望看到整个列表在没有任何零的情况下得到处理。 有什么提示吗?正如Leo所说,您不希望同时运行请求。正如上面所说: 启动反向地理编码请求后,不要尝试启动另一个反向或正向地理编码

我有一个位置列表(大约30个元素):

我的目的是从该列表中获取街道名称,因此我决定使用
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")
            })
        }
    }
    
  • 如果以iOS 13及更高版本为目标,您可以使用,例如,为反向地理编码定义发布者:

    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")
            })
        }
    }
    
  • 如果以iOS 13及更高版本为目标,您可以使用,例如,为反向地理编码定义发布者:

    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