Swift中viewWillAppear赋值后变量仍为nil的问题排查与解决
Swift async/await异步赋值后tableView中变量为nil的原因及解决方法
问题描述
我是Swift async/await的新手,尝试在MyViewController的viewWillAppear中调用异步函数为全局变量job赋值。在异步函数内部job能获取到值,但在tableView的cellForRowAt方法中给cell属性传值时,job却为nil。
示例代码
class MyViewController: UIViewController { private var job: Job! override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Task { [weak self] in await self?.getJob() } } private func getJob() async { job = await viewModel.getJob(byID: "xxxxxxx") // will return a Job Object // more code... print(job) // it's ok } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { print(job) // it is nil!!! } }
原因分析
- 异步时序不匹配:
viewWillAppear中启动的Task是异步执行的,await viewModel.getJob(byID:)需要等待异步任务(比如网络请求)完成才会给job赋值。但tableView的cellForRowAt会在视图加载完成后立即触发,此时job还未完成赋值,所以输出nil。 - 隐式解包可选的初始状态:
job被声明为Job!(隐式解包可选),初始值为nil。在异步赋值完成前访问它,会直接返回nil,甚至可能因强制解包引发崩溃。
解决方法
1. 赋值完成后主线程刷新tableView
异步任务完成并赋值job后,必须回到主线程刷新tableView,确保cell能获取到最新数据:
private func getJob() async { job = await viewModel.getJob(byID: "xxxxxxx") print(job) // 主线程刷新tableView DispatchQueue.main.async { self.tableView.reloadData() } }
2. 替换隐式解包为普通可选类型
将job改为普通可选类型,避免隐式解包的风险,并在cellForRowAt中做安全校验:
// 修改变量声明 private var job: Job? func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "YourCellID", for: indexPath) as! YourCellType // 安全访问job if let job = job { // 给cell赋值 cell.titleLabel.text = job.title // 其他配置... } return cell }
3. 管理异步任务生命周期(可选)
为了避免视图消失后仍执行不必要的异步任务,可添加任务引用并在视图消失时取消:
private var jobFetchTask: Task<Void, Never>? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 取消之前未完成的任务 jobFetchTask?.cancel() jobFetchTask = Task { [weak self] in await self?.getJob() } } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) jobFetchTask?.cancel() }
内容的提问来源于stack exchange,提问作者keto895
相关产品推荐
相关产品推荐

