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

用户接受位置权限提示后CurrentLocation为nil的iOS问题如何解决

问题根因

崩溃的核心原因有两个:

  1. 你当前的LocationManager封装只处理了权限状态的等待,完全没处理定位成功的异步过程:权限授权通过不代表定位已经拿到结果,此时CLLocationManager的location属性默认是nil,你用隐式解包的CLLocation!去接当然会崩溃。第二次启动时因为系统已经有权限,且之前的定位缓存还在,所以location有值不会崩。
  2. 你用OperationQueue的实现逻辑有漏洞:权限通过就直接把队列放开,队列里的任务立刻执行,完全不等定位结果返回。
优化后的实现方案

第一步:改造LocationManager单例,支持定位结果回调

直接把定位请求、权限判断、结果返回都封装在单例内部,对外只暴露获取坐标的方法,不需要业务层处理权限和定位逻辑:

import Foundation
import CoreLocation

class LocationManager: NSObject, CLLocationManagerDelegate {
    static let sharedInstance = LocationManager()
    private var locationManager = CLLocationManager()
    // 存储所有待执行的定位完成回调
    private var locationCallbacks: [(Result<CLLocation, Error>) -> Void] = []
    // 标记是否正在定位中,避免重复发起请求
    private var isLocating = false
    
    enum LocationError: Error {
        case permissionDenied
        case locateFailed
    }

    override init(){
        super.init()
        locationManager.delegate = self
        // 可以根据你的业务需求调整定位精度
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
    }
    
    // 对外暴露的唯一方法:获取当前坐标
    func fetchCurrentLocation(callback: @escaping (Result<CLLocation, Error>) -> Void) {
        let authStatus = CLLocationManager.authorizationStatus()
        // 先处理权限情况
        switch authStatus {
        case .notDetermined:
            // 权限未申请,先存回调,再申请权限
            locationCallbacks.append(callback)
            locationManager.requestWhenInUseAuthorization()
        case .authorizedWhenInUse, .authorizedAlways:
            // 权限已给,存回调,发起定位
            locationCallbacks.append(callback)
            startLocation()
        case .denied, .restricted:
            // 权限被拒,直接返回错误
            callback(.failure(LocationError.permissionDenied))
        @unknown default:
            callback(.failure(LocationError.locateFailed))
        }
    }
    
    private func startLocation() {
        guard !isLocating else { return }
        isLocating = true
        locationManager.startUpdatingLocation()
    }
    
    // 权限变更回调
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .authorizedWhenInUse, .authorizedAlways:
            // 权限通过,发起定位
            startLocation()
        case .denied, .restricted:
            // 权限被拒,回调所有等待的任务返回错误
            isLocating = false
            locationCallbacks.forEach { $0(.failure(LocationError.permissionDenied)) }
            locationCallbacks.removeAll()
        default:
            break
        }
    }
    
    // 定位成功回调
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        // 停止定位节省功耗
        locationManager.stopUpdatingLocation()
        isLocating = false
        // 把定位结果回调给所有等待的业务方
        locationCallbacks.forEach { $0(.success(location)) }
        locationCallbacks.removeAll()
    }
    
    // 定位失败回调
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        locationManager.stopUpdatingLocation()
        isLocating = false
        locationCallbacks.forEach { $0(.failure(LocationError.locateFailed)) }
        locationCallbacks.removeAll()
    }
}

第二步:业务层调用逻辑调整

你原来的业务代码直接替换成以下写法即可,不需要再处理权限判断、定位是否为空的逻辑:

LocationManager.sharedInstance.fetchCurrentLocation { [weak self] result in
    switch result {
    case .success(let currentLocation):
        // 拿到坐标直接发起请求,不会为空
        let latitude = currentLocation.coordinate.latitude
        let longitude = currentLocation.coordinate.longitude
        // 拼接你的请求参数,发起API请求
        SessionManager.manager.request(url, parameters: ["lat": latitude, "lon": longitude]).responseJSON { responseData in
            // 解析数据,刷新页面
            callback()
        }
    case .failure(let error):
        // 这里可以处理权限被拒、定位失败的情况,比如默认返回热门城市的演出数据,或者弹提示引导用户开权限
        print("定位失败:\(error)")
    }
}

额外注意点

  • 不要用sleep等待异步结果,sleep会卡住当前线程,完全起不到等待定位的作用,反而会让App卡住无响应
  • 业务层不要直接访问CLLocationManager.location属性,这个属性是系统缓存的定位结果,可能为空,也可能是很久之前的过期定位,必须通过didUpdateLocations回调拿最新的有效定位
  • info.plist里一定要添加NSLocationWhenInUseUsageDescription的权限描述,否则申请权限会直接崩溃

内容的提问来源于stack exchange,提问作者mrbenhill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.15 03:54:07