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

自定义LocationManager无法获取更新位置问题排查

自定义LocationManager无法获取位置的问题排查与修复

我创建了自定义LocationManager类,希望能在App任意位置获取用户位置,但调用该类实例无法获取位置。我已授权设备位置权限,日志仅打印“Location allowed”,但didUpdateLocations方法未触发,尝试将locationManager.startUpdatingLocation()替换为locationManager.requestLocation()也无效。以下是我的代码:

LocationManager.swift代码

import CoreLocation

class LocationManager: NSObject {
    private var locationManager: CLLocationManager?
    private var locationCallback: ((CLLocation?, String?) -> Void)?
    
    override init() {
        super.init()
        print("initlocmen")
        self.locationManager = CLLocationManager()
        self.locationManager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        self.locationManager?.delegate = self
    }
    
    func requestLocationAndCity(callback: @escaping (CLLocation?, String?) -> Void) {
        self.locationCallback = callback
        
        guard let locationManager = locationManager else {
            return
        }
        
        if CLLocationManager.locationServicesEnabled() {
            let status = CLLocationManager.authorizationStatus()
            
            if status == .authorizedAlways || status == .authorizedWhenInUse {
//                locationManager.requestLocation()
                print("Location allowed")
                locationManager.startUpdatingLocation()
            } else if status == .notDetermined {
                locationManager.requestWhenInUseAuthorization()
            } else if status == .denied || status == .restricted {
                callback(nil, nil)
            } else {
                callback(nil, nil)
            }
        } else {
            callback(nil, nil)
        }
    }
}

extension LocationManager: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        locationManager?.stopUpdatingLocation()
        print("didUpdateLocations")
        if let location = locations.last {
            // Reverse geocode the location to get the city
            CLGeocoder().reverseGeocodeLocation(location) { [weak self] placemarks, error in
                if let city = placemarks?.first?.locality {
                    self?.locationCallback?(location, city)
                } else {
                    self?.locationCallback?(location, nil)
                }
            }
        } else {
            locationCallback?(nil, nil)
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Error getting location:", error)
        locationCallback?(nil, nil)
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        if status == .authorizedWhenInUse || status == .authorizedAlways {
            locationManager?.requestLocation()
        } else {
            locationCallback?(nil, nil)
        }
    }
}

视图控制器调用代码

let locationManager = LocationManager()
locationManager.requestLocationAndCity { location, city in
    print("user city", city ?? "none")
}

问题原因

  • 核心问题:LocationManager实例被提前释放
    视图控制器中创建的locationManager是局部变量,方法执行完毕后会被ARC自动回收。此时内部的CLLocationManager还未完成位置请求,代理对象已不存在,自然无法触发didUpdateLocations等代理方法。
  • 次要隐患:Info.plist权限描述缺失(若未配置,即使授权成功也可能被系统限制位置获取)

修复方案

方案1:将自定义LocationManager改为单例(推荐,适合全局调用)

通过单例模式确保实例全局存在,不会被提前释放:

import CoreLocation

class LocationManager: NSObject {
    // 全局单例实例
    static let shared = LocationManager()
    
    private var locationManager: CLLocationManager?
    private var locationCallback: ((CLLocation?, String?) -> Void)?
    
    // 私有初始化方法,禁止外部创建实例
    private override init() { 
        super.init()
        print("initlocmen")
        self.locationManager = CLLocationManager()
        self.locationManager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
        self.locationManager?.delegate = self
    }
    
    // 其余方法保持不变...
}

调用时使用单例:

LocationManager.shared.requestLocationAndCity { location, city in
    print("user city", city ?? "none")
}

方案2:在视图控制器中强持有实例

若不需要全局使用,在视图控制器中用属性强引用持有LocationManager:

class YourViewController: UIViewController {
    // 强引用持有实例,避免被ARC回收
    private let locationManager = LocationManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        locationManager.requestLocationAndCity { location, city in
            print("user city", city ?? "none")
        }
    }
}

额外检查与优化

  1. 确认Info.plist权限配置
    必须添加至少一个权限描述键值对(根据你请求的权限类型):
    • NSLocationWhenInUseUsageDescription:描述App在前台使用时需要位置权限的原因
    • NSLocationAlwaysAndWhenInUseUsageDescription:描述App需要始终获取位置权限的原因
  2. 完善反向地理编码错误处理
    在reverseGeocodeLocation回调中添加错误捕获,避免静默失败:
    CLGeocoder().reverseGeocodeLocation(location) { [weak self] placemarks, error in
        if let error = error {
            print("反向地理编码错误:", error)
            self?.locationCallback?(location, nil)
            return
        }
        let city = placemarks?.first?.locality
        self?.locationCallback?(location, city)
    }
    
  3. requestLocation()使用注意
    使用requestLocation()时,必须同时实现didUpdateLocations和didFailWithError代理方法,否则系统会抛出异常;该方法仅会请求一次位置,适合单次获取场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 19:50:13