这段Swift定位相关代码是否存在竞态条件?当前实现是否能正确执行?
你当前的实现方案存在本质的竞态问题,仅靠DispatchQueue.main.async包裹只是碰巧让请求执行时机延后,刚好赶上定位赋值完成,并没有从根源解决问题。一旦遇到定位回调延迟的场景(比如首次冷启动定位、信号差),还是会出现请求发出时经纬度仍为默认值0,0的情况。
正确的实现逻辑应该严格遵循你的需求:将postRequest()的调用移到didUpdateLocations回调中,同时增加有效性判断、去重逻辑和超时降级机制,具体实现如下:
步骤1:新增必要属性
// 标记是否已经发起过请求,避免didUpdateLocations多次回调重复触发 private var hasPostedLocation = false // 定位超时计时器,避免长时间等待不到有效位置 private var locationTimeoutTimer: Timer?
步骤2:调整didChangeAuthorizationStatus逻辑
移除其中的postRequest()调用,仅保留定位启动逻辑和超时计时器初始化:
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { switch status { case .notDetermined: // 原有逻辑保留 break case .authorizedWhenInUse, .authorizedAlways: // 重置标记,允许本次授权后发起新请求 hasPostedLocation = false primaryManager!.startUpdatingLocation() // 初始化10秒超时,可根据业务需求调整时长 locationTimeoutTimer = Timer.scheduledTimer(withTimeInterval: 10, repeats: false) { [weak self] _ in guard let self = self, !self.hasPostedLocation else { return } self.primaryManager?.stopUpdatingLocation() // 此处可添加定位超时降级逻辑:比如提示用户定位失败、使用缓存位置/IP定位等 } break case .restricted, .denied: // 原有逻辑保留 break default: break } }
步骤3:改造didUpdateLocations回调
在确认拿到有效位置后再发起请求:
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // 过滤掉空位置、已经发起过请求的情况 guard let latestLocation = locations.last, !hasPostedLocation else { return } // 位置有效性校验:排除默认0值、精度不符合要求的位置(精度阈值可自行调整) guard latestLocation.coordinate.latitude != 0, latestLocation.coordinate.longitude != 0, latestLocation.horizontalAccuracy > 0, latestLocation.horizontalAccuracy < 100 else { return } // 原有用户位置赋值逻辑保留 // userLat = latestLocation.coordinate.latitude // userLon = latestLocation.coordinate.longitude // 停止定位节省电量 primaryManager?.stopUpdatingLocation() // 取消超时计时器 locationTimeoutTimer?.invalidate() locationTimeoutTimer = nil // 标记已发起请求 hasPostedLocation = true // 发起请求,CLLocationManager默认回调在主队列,无需额外包裹DispatchQueue.main.async functionMaster.postRequest() }
补充说明
- 如果你的业务需要每次定位更新都同步上报位置,去掉
hasPostedLocation的相关判断即可 - 如果你手动设置了CLLocationManager的delegate执行队列非主队列,涉及UI操作或全局属性赋值的代码需要手动切回主队列执行
- 经纬度的有效性校验可以根据业务场景调整,比如不需要高精度的话可以把水平精度阈值放大到500米甚至更高
内容的提问来源于stack exchange,提问作者nickcoding2
相关产品推荐
相关产品推荐

