You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何调用startUpdatingLocation后CLLocationManager有时会更新两次位置?

问题分析与解决方案

你的问题核心是多次触发locationUpdated回调,导致fetchPlaces被执行两次。主要原因有两个:

  1. 授权状态回调和主动请求授权的逻辑中,可能重复调用startUpdatingLocation()
  2. 即使调用了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:40:25