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

如何保留@escaping闭包的返回结果,避免数据丢失?

问题:闭包外变量无法保存异步请求返回的数据

我需要实现一个接收邮政编码(postalCode)并返回城市、州和国家的函数。目前函数能成功获取结果,在调用的@escaping闭包内可以正常打印数据,但把数据保存到闭包外的变量后,数据就丢失了。

我的代码

func getAddress(forPostalCode postalCode: String, completion: @escaping (_ city: String?, _ state: String?, _ country: String?, _ error: Error?) -> Void) {
    let geocoder = CLGeocoder()
    let addressString = "\(postalCode), USA"
    geocoder.geocodeAddressString(addressString) { placemarks, error in
        if let error = error {
            completion(nil, nil, nil, error)
            return
        }
        guard let placemark = placemarks?.first else {
            completion(nil, nil, nil, NSError(domain: "com.example.app", code: 1, userInfo: [NSLocalizedDescriptionKey: "No placemarks found"]))
            return
        }
        guard let city = placemark.locality else {
            completion(nil, nil, nil, NSError(domain: "com.example.app", code: 2, userInfo: [NSLocalizedDescriptionKey: "City not found"]))
            return
        }
        guard let state = placemark.administrativeArea else {
            completion(nil, nil, nil, NSError(domain: "com.example.app", code: 3, userInfo: [NSLocalizedDescriptionKey: "State not found"]))
            return
        }
        guard let country = placemark.country else {
            completion(nil, nil, nil, NSError(domain: "com.example.app", code: 4, userInfo: [NSLocalizedDescriptionKey: "Country not found"]))
            return
        }
        completion(city, state, country, nil)
    }
}

let postalCode = "10001"
var aCity: String = ""
var aState: String = ""
var aCountry: String = ""

getAddress(forPostalCode: postalCode) { city, state, country, error in
    if let error = error {
        print("Error: \(error.localizedDescription)")
        return
    }
    if let city = city, let state = state, let country = country {
        aCity = city
        aState = state
        aCountry = country
        print("Internal: \(aCity), \(aState) in \(aCountry)")
    }
    else {
        print("Error: Unable to retrieve address for postal code \(postalCode)")
    }
}

print("External: \(aCity), \(aState) in \(aCountry)")

运行结果

External: ,  in 
Internal: New York, NY in United States

解答

这是因为geocodeAddressString是异步操作,它会在后台线程执行地理编码请求,不会阻塞主线程。代码的实际执行顺序是:

  1. 初始化aCity、aState、aCountry为空字符串
  2. 调用getAddress,发起异步地理编码请求
  3. 直接执行闭包外的print("External: ..."),此时异步请求还未完成,变量仍为初始空值
  4. 异步请求完成后,才会进入闭包内部给变量赋值并打印Internal: ...

解决方案

所有依赖异步请求结果的操作,必须放在闭包内部执行,或者使用Swift现代的async/await语法处理异步逻辑:

方案1:在闭包内处理结果(最直接)

把需要使用地址数据的逻辑全部移到闭包内部:

getAddress(forPostalCode: postalCode) { city, state, country, error in
    if let error = error {
        print("Error: \(error.localizedDescription)")
        return
    }
    guard let city = city, let state = state, let country = country else {
        print("Error: Unable to retrieve address for postal code \(postalCode)")
        return
    }
    // 在这里执行所有需要用到地址数据的操作
    print("Address: \(city), \(state) in \(country)")
    // 比如更新UI、保存到本地等
    // updateUserInterface(city: city, state: state, country: country)
}

方案2:改用async/await(更简洁的异步写法)

将原函数改造为支持async/await的版本,避免闭包嵌套:

import CoreLocation

func getAddress(forPostalCode postalCode: String) async throws -> (city: String, state: String, country: String) {
    let geocoder = CLGeocoder()
    let addressString = "\(postalCode), USA"
    
    let placemarks = try await geocoder.geocodeAddressString(addressString)
    guard let placemark = placemarks.first else {
        throw NSError(domain: "com.example.app", code: 1, userInfo: [NSLocalizedDescriptionKey: "No placemarks found"])
    }
    guard let city = placemark.locality else {
        throw NSError(domain: "com.example.app", code: 2, userInfo: [NSLocalizedDescriptionKey: "City not found"])
    }
    guard let state = placemark.administrativeArea else {
        throw NSError(domain: "com.example.app", code: 3, userInfo: [NSLocalizedDescriptionKey: "State not found"])
    }
    guard let country = placemark.country else {
        throw NSError(domain: "com.example.app", code: 4, userInfo: [NSLocalizedDescriptionKey: "Country not found"])
    }
    return (city, state, country)
}

// 调用示例(需在异步上下文,比如Task中)
let postalCode = "10001"
Task {
    do {
        let address = try await getAddress(forPostalCode: postalCode)
        print("Address: \(address.city), \(address.state) in \(address.country)")
        // 在这里处理地址数据
    } catch {
        print("Error: \(error.localizedDescription)")
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 19:23:16