Swift:如何在viewDidLoad前完成委托设置?Firestore数据加载报错解决
问题解决思路
一、把parcelDataModel.vc = self放到viewDidLoad之前的可行方案
方案1:用懒加载初始化ViewModel
把var parcelDataModel = MainViewModel()改成懒加载形式,这样第一次访问parcelDataModel时,self已经完成初始化,能安全完成vc的赋值:
lazy var parcelDataModel: MainViewModel = { let model = MainViewModel() model.vc = self return model }()
这样你就不用在viewDidLoad里重复写self.parcelDataModel.vc = self,且赋值会在viewDidLoad之前触发(首次访问model时,通常viewDidLoad里会调用它的方法,自然触发懒加载)。
方案2:在VC的初始化器里赋值
如果你的VC是代码初始化的,重写init(nibName:bundle:);如果是Storyboard/XIB创建的,重写init?(coder:):
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) self.parcelDataModel.vc = self } required init?(coder: NSCoder) { super.init(coder: coder) self.parcelDataModel.vc = self }
注意:这种方式要确保parcelDataModel的初始化不依赖self的其他未初始化属性,你当前的parcelDataModel是默认初始化的,完全没问题。
二、重要提醒:避免循环引用
你的MainViewModel里的vc属性必须用弱引用,否则会导致VC和ViewModel互相持有,引发内存泄漏:
class MainViewModel { weak var vc: ParcelViewController? // 用weak修饰,设为可选类型 // ...其他业务代码 }
三、顺便解决UITableView的nil解包错误
你遇到的表格数据nil解包问题,大概率是Firestore数据获取是异步操作,数据还没返回时表格就执行了数据源方法,或者ViewModel拿到数据后没通知VC刷新表格。
解决方法:
- 在
MainViewModel的getCollectionData()方法中,数据获取完成后通知VC刷新表格:
// MainViewModel中的代码 func getCollectionData() { Firestore.firestore().collection("parcels").getDocuments { snapshot, error in // 解析数据并赋值给self.parcels self.parcels = ... // 你的数据解析逻辑 // 回到主线程刷新表格 DispatchQueue.main.async { self.vc?.parcelTable.reloadData() } } }
- 在数据源方法中增加安全判断,避免数组越界:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = parcelTable.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ParcelTableViewCell guard indexPath.row < parcelDataModel.parcels.count else { return cell } let current = self.parcelDataModel.parcels[indexPath.row] cell.parcelTitleLabel.text = current.parcel_type cell.parcelImage.image = UIImage(named: current.parcel_img_url) cell.parcelWeightlabel.text = "\(current.parcel_min_weight) - \(current.parcel_max_weight)" cell.parcelDescriptionLabel.text = current.parcel_description return cell }
内容的提问来源于stack exchange,提问作者mohammadgdz
相关产品推荐
相关产品推荐

