iOS开发:初始化TableView数据源数组调用UserDefaults报错的解决
问题场景
开发App天气功能时,通过Visual Crossing API获取数据,在didUpdateWeather函数中将数据存入UserDefaults:
let date1ForTableView = UserDefaults.standard func didUpdateWeather(weather: WeatherModel) { DispatchQueue.main.async { self.date1ForTableView.set(weather.datetime1, forKey: "Date1") // ...其他代码 } }
但初始化TableView数据源数组WeatherForecastCells时,出现错误:
Cannot use instance member 'date1ForTableView' within property initialiser; property initialisers run before 'self' is available
初始化代码如下:
var WeatherForecastCells: [WeatherCell] = [ WeatherCell(image: "", date: date1ForTableView.string(forKey: "Date1")!, low: "18°", high: "31°"), // ...其他元素 ]
已知定位输入后数据会自动保存,附TableView数据源扩展代码:
extension WeatherFeatures: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return WeatherForecastCells.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "WeatherCellsForWeatherFeature", for: indexPath) as! WeatherForecastTableViewCell cell.WeatherDetails.text = "\(WeatherForecastCells[indexPath.row].date) - Low: \(WeatherForecastCells[indexPath.row].low) High: \(WeatherForecastCells[indexPath.row].high)" return cell } }
解决方案
1. 直接使用UserDefaults.standard初始化数组
既然date1ForTableView只是UserDefaults.standard的实例引用,完全可以跳过这个变量,直接在数组初始化时调用UserDefaults.standard,绕过实例成员访问限制:
var WeatherForecastCells: [WeatherCell] = [ WeatherCell(image: "", date: UserDefaults.standard.string(forKey: "Date1")!, low: "18°", high: "31°"), // ...其他元素 ]
这是最直接的解决方式,不需要修改其他代码结构。
2. 用懒加载延迟数组初始化
将WeatherForecastCells声明为lazy属性,这样初始化逻辑会推迟到第一次访问该属性时执行,此时self已经完全初始化:
lazy var WeatherForecastCells: [WeatherCell] = [ WeatherCell(image: "", date: date1ForTableView.string(forKey: "Date1")!, low: "18°", high: "31°"), // ...其他元素 ]
注意:懒加载属性默认线程不安全,UI场景下主线程访问无需额外处理;如果后续要更新数据,需要重新赋值数组并刷新TableView。
3. 声明可选数组,在viewDidLoad或数据更新时初始化
把数组改成可选类型,在viewDidLoad(或数据回调后)完成初始化,确保self可用:
var WeatherForecastCells: [WeatherCell]? override func viewDidLoad() { super.viewDidLoad() // 初始化数据源数组 WeatherForecastCells = [ WeatherCell(image: "", date: date1ForTableView.string(forKey: "Date1")!, low: "18°", high: "31°"), // ...其他元素 ] tableView.reloadData() }
同时需要修改数据源方法,处理可选值避免崩溃:
extension WeatherFeatures: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return WeatherForecastCells?.count ?? 0 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "WeatherCellsForWeatherFeature", for: indexPath) as! WeatherForecastTableViewCell guard let cellData = WeatherForecastCells?[indexPath.row] else { return cell } cell.WeatherDetails.text = "\(cellData.date) - Low: \(cellData.low) High: \(cellData.high)" return cell } }
4. 数据更新后刷新数据源
无论用哪种方案,当didUpdateWeather中保存完新数据后,都需要重新初始化数组并刷新TableView,确保界面显示最新数据:
func didUpdateWeather(weather: WeatherModel) { DispatchQueue.main.async { self.date1ForTableView.set(weather.datetime1, forKey: "Date1") // 重新初始化数据源数组 self.WeatherForecastCells = [ WeatherCell(image: "", date: self.date1ForTableView.string(forKey: "Date1")!, low: "18°", high: "31°"), // ...其他元素 ] self.tableView.reloadData() } }
内容的提问来源于stack exchange,提问作者Train Lover

