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

Swift中如何让@Published变量正确更新?附地址转坐标问题

问题分析

你遇到的不是@Published变量没被赋值,而是异步操作的执行顺序问题:

  • getCoordinate里的geocodeAddressString是异步方法,它会在后台线程执行地理编码,完成后才会调用completion闭包。
  • 你在saveMapData里调用convertAddress后立刻打印userAddressLat和userAddressLong,此时异步的地理编码任务还没完成,变量还是初始值0.0。而convertAddress闭包里的打印是在异步任务完成后执行的,所以能拿到正确值。
解决方案

有两种常见的修复方式,根据你的iOS版本选择:

方式1:给convertAddress添加Completion Handler

修改convertAddress方法,让它完成编码后通知调用者,这样你就能在正确的时机保存数据:

final class PublishMapData: ObservableObject {
    
    @Published var userAddressLat: Double = 0.0
    @Published var userAddressLong: Double = 0.0
    
    func saveMapData(address: String){
        let address = "One Apple Park Way, Cupertino, CA 95014"
        convertAddress(address: address) { [weak self] in
            guard let self = self else { return }
            // 这里异步任务已经完成,可以拿到正确的坐标值
            print(String(self.userAddressLat))
            print(String(self.userAddressLong))
            // 在这里执行存入Firestore的操作
        }
    }
    
    // 添加completion参数
    func convertAddress(address: String, completion: @escaping () -> Void) {
        getCoordinate(addressString: address) { (location, error) in
            if error != nil {
                completion()
                return
            }
            DispatchQueue.main.async {
                self.userAddressLat = location.latitude
                self.userAddressLong = location.longitude
                completion() // 编码完成后触发回调
            }
        }
    }

    private func getCoordinate(addressString : String, completionHandler: @escaping(CLLocationCoordinate2D, NSError?) -> Void ) {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(addressString) { (placemarks, error) in
            if error == nil {
                if let placemark = placemarks?[0] {
                    let location = placemark.location!
                    completionHandler(location.coordinate, nil)
                    return
                }
            }
            completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
        }
    }
}

方式2:使用Async/Await(iOS 15+、macOS 12+)

如果你的项目支持较新的系统版本,可以用Swift的并发框架简化异步代码,避免嵌套闭包:

import CoreLocation

final class PublishMapData: ObservableObject {
    
    @Published var userAddressLat: Double = 0.0
    @Published var userAddressLong: Double = 0.0
    
    // 标记为async方法
    func saveMapData(address: String) async {
        let address = "One Apple Park Way, Cupertino, CA 95014"
        do {
            let coordinate = try await convertAddress(address: address)
            userAddressLat = coordinate.latitude
            userAddressLong = coordinate.longitude
            // 这里可以直接执行存入Firestore的操作
            print(String(userAddressLat))
            print(String(userAddressLong))
        } catch {
            print("地理编码失败:\(error)")
        }
    }
    
    // 转为async throws方法
    func convertAddress(address: String) async throws -> CLLocationCoordinate2D {
        return try await withCheckedThrowingContinuation { continuation in
            getCoordinate(addressString: address) { (location, error) in
                if let error = error {
                    continuation.resume(throwing: error)
                } else {
                    continuation.resume(returning: location)
                }
            }
        }
    }

    private func getCoordinate(addressString : String, completionHandler: @escaping(CLLocationCoordinate2D, NSError?) -> Void ) {
        let geocoder = CLGeocoder()
        geocoder.geocodeAddressString(addressString) { (placemarks, error) in
            if error == nil {
                if let placemark = placemarks?[0] {
                    let location = placemark.location!
                    completionHandler(location.coordinate, nil)
                    return
                }
            }
            completionHandler(kCLLocationCoordinate2DInvalid, error as NSError?)
        }
    }
}

调用saveMapData时需要在async上下文里:

// 比如在View的task修饰符里
.task {
    let mapData = PublishMapData()
    await mapData.saveMapData(address: "测试地址")
}
额外注意点
  • 使用[weak self]避免循环引用:在闭包里引用self时,建议加上[weak self]防止内存泄漏,尤其是在ObservableObject这类可能被View持有对象中。
  • 错误处理:当前代码里忽略了错误情况,建议在编码失败时添加提示或处理逻辑,比如给用户显示错误弹窗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:11:32