为何调用startUpdatingLocation后CLLocationManager有时会更新两次位置?
问题分析与解决方案
你的问题核心是多次触发locationUpdated回调,导致fetchPlaces被执行两次。主要原因有两个:
- 授权状态回调和主动请求授权的逻辑中,可能重复调用
startUpdatingLocation() - 即使调用了
stopUpdatingLocation(),系统可能已经有多个位置更新事件在队列中等待处理,导致didUpdateLocations被多次调用
修复方案
1. 给LocationService添加状态锁,防止重复启动位置更新
在单例类中增加一个布尔标记,确保同一时间只有一个位置更新请求在进行:
class LocationService: NSObject { static let shared = LocationService() lazy var locationManager: CLLocationManager = { let manager = CLLocationManager() manager.desiredAccuracy = kCLLocationAccuracyBest manager.delegate = self return manager }() var locationUpdated: ((CLLocationCoordinate2D) -> Void)? // 添加状态锁,避免重复启动位置更新 private var isUpdatingLocation = false override private init() { super.init() } func requestPermissionToAccessLocation() { guard !isUpdatingLocation else { return } // 已在更新时直接返回 switch locationManager.authorizationStatus { case .notDetermined: locationManager.requestWhenInUseAuthorization() case .restricted: locationManager.requestWhenInUseAuthorization() case .denied: break case .authorizedAlways, .authorizedWhenInUse: startLocationUpdate() default: break } } // 抽离启动逻辑,统一管理状态 private func startLocationUpdate() { isUpdatingLocation = true locationManager.startUpdatingLocation() } }
2. 在didUpdateLocations中确保只处理一次回调,并重置状态
修改代理方法,处理完位置后立即重置状态,同时避免队列中残留事件触发重复回调:
extension LocationService: CLLocationManagerDelegate { func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { manager.stopUpdatingLocation() // 确保只处理一次有效回调 guard isUpdatingLocation, let location = locations.last?.coordinate else { return } isUpdatingLocation = false print(location) locationUpdated?(location) // 处理完后清空闭包,防止后续意外触发 locationUpdated = nil } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { print(error.localizedDescription) isUpdatingLocation = false locationUpdated = nil } func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { guard !isUpdatingLocation else { return } switch manager.authorizationStatus { case .authorizedAlways, .authorizedWhenInUse: startLocationUpdate() default: break } } }
3. 按钮点击时重置旧回调
在getUserLocation中,每次点击前先清空之前的闭包,防止旧的回调被意外触发:
func getUserLocation() { self.places.removeAll() self.placesTableView.reloadData() // 先清空旧的回调,避免重复触发 LocationService.shared.locationUpdated = nil LocationService.shared.requestPermissionToAccessLocation() LocationService.shared.locationUpdated = { [weak self] location in self?.fetchPlaces(location: location) } // ... 你的UI布局代码 }
额外优化建议
如果需要更高精度的单次定位,可以从locations数组中筛选精度最高的位置,而不是直接取最后一个:
// 在didUpdateLocations中替换取坐标的逻辑 let bestLocation = locations.sorted { $0.horizontalAccuracy < $1.horizontalAccuracy }.first guard let location = bestLocation?.coordinate else { return }
内容的提问来源于stack exchange,提问作者BiggRojo
相关产品推荐
相关产品推荐

