Swift中全局变量bank在viewDidLoad值为0,JSON序列化时为1的问题
问题根源:异步执行顺序搞的鬼!
嘿,这个坑我踩过好多次!你遇到的情况几乎可以肯定是异步任务的执行顺序导致的——咱们来一步步拆解到底发生了什么:
1. 先理清代码的执行流程
你定义的全局变量var bank = Int()默认值是0,然后调用getProfile()方法。但这里的关键是:getProfile()里的JSON解析操作,大概率是在异步任务里完成的(比如网络请求的回调、后台队列的处理)。
举个最典型的场景,你的getProfile()可能是这样写的:
func getProfile() { // 假设这是一个异步网络请求(比如URLSession的dataTask) URLSession.shared.dataTask(with: someProfileURL) { data, response, error in guard let data = data else { return } // 解析JSON let jsonResult = try! JSONSerialization.jsonObject(with: data) as! [String: Any] let banks = jsonResult["banks"] as! Int print(banks) // 这里打印出1 self.bank = banks // 赋值操作在异步闭包里 }.resume() }
这时候代码的执行顺序是:
- 视图加载时,
viewDidLoad()先执行,你调用了getProfile() getProfile()发起异步任务(比如网络请求),但主线程不会等它完成,直接继续往下走,执行print(bank)——这时候bank还是默认的0- 等异步任务完成(比如网络请求拿到数据),才会执行闭包里的代码,把
bank赋值为1,但这时候viewDidLoad()里的打印早就跑完了!
2. 怎么验证这个猜想?
你可以在两个地方加打印日志,看看输出顺序:
// 在getProfile的赋值处加打印 self.bank = banks print("✅ 赋值完成!当前bank值:\(self.bank)") // 在viewDidLoad里加打印 override func viewDidLoad() { super.viewDidLoad() getProfile() print("📱 viewDidLoad执行中,当前bank值:\(bank)") }
运行后你会发现,viewDidLoad的打印会先出现(显示0),过一会儿才会出现赋值完成的打印(显示1)——这就实锤了顺序问题!
3. 怎么解决?
核心思路是:等异步任务完成后,再去使用bank变量,这里给你两种常用的方案:
方案一:用闭包回调
改造getProfile(),添加一个完成回调,在异步任务结束后通知主线程:
func getProfile(completion: @escaping (Int) -> Void) { URLSession.shared.dataTask(with: someProfileURL) { data, _, error in guard let data = data, error == nil else { completion(0) // 出错时返回默认值 return } do { let jsonResult = try JSONSerialization.jsonObject(with: data) as! [String: Any] let banks = jsonResult["banks"] as! Int // 切回主线程更新变量或UI DispatchQueue.main.async { completion(banks) } } catch { completion(0) } }.resume() }
然后在viewDidLoad()里这样用:
override func viewDidLoad() { super.viewDidLoad() getProfile { [weak self] bankValue in self?.bank = bankValue print("现在bank的值是:\(self?.bank ?? 0)") // 所有需要用到bank的逻辑都放在这里 } }
方案二:用Swift的Async/Await(Swift 5.5+)
如果你的项目支持Swift 5.5及以上,用异步等待会更简洁:
// 把getProfile改成异步函数 func getProfile() async throws -> Int { let (data, _) = try await URLSession.shared.data(from: someProfileURL) let jsonResult = try JSONSerialization.jsonObject(with: data) as! [String: Any] return jsonResult["banks"] as! Int }
然后在viewDidLoad()里用Task调用:
override func viewDidLoad() { super.viewDidLoad() // 启动一个异步任务 Task { do { let bankValue = try await getProfile() self.bank = bankValue print("现在bank的值是:\(self.bank)") // 后续依赖bank的操作放在这里 } catch { print("获取数据出错:\(error)") } } }
不管哪种方案,核心都是确保你在bank被正确赋值之后,再去使用它,而不是在异步任务还没完成时就急于打印或使用。
内容的提问来源于stack exchange,提问作者user9368327
相关产品推荐
相关产品推荐

