Swift中TableViewCell无法调用父ViewController方法的问题
Swift中MultipleAnswerTableViewCell调用父控制器方法触发nil错误的解决
问题场景
开发中MultipleAnswerTableViewCell调用父控制器AddQuestionViewController的savedAnswer方法时,触发致命错误:Unexpectedly found nil while implicitly unwrapping an Optional value。已在父控制器的configureMultiAnswerCell方法中为cell的parentVC属性赋值self,但问题依旧,怀疑saveButtonTapped方法内的重新dequeueCell操作是诱因。
核心代码片段
MultipleAnswerTableViewCell关键代码
class MultipleAnswerTableViewCell: UITableViewCell, SurveyAnswerViewDelegate, UITextFieldDelegate { // 隐式可选的父控制器引用 var parentVC: AddQuestionViewController! // 触发错误的代码 let surveyAnswer = SurveyAnswer(answer: textField.text!, order: Int(textField.tag)) surveyAnswers.append(surveyAnswer) self.parentVC?.savedAnswer(answer: surveyAnswer) // 此处触发nil解包错误 // 尝试可选绑定但仍无效 if let myParentVC = self.parentVC as? AddQuestionViewController { myParentVC.savedAnswer(answer: surveyAnswer) } }
AddQuestionViewController关键代码
class AddQuestionViewController: BaseViewController, SurveyTopTableViewCellDelegate, MultipleAnswerTableViewCellDelegate, SurveyTextAnswerTableViewCellDelegate, UITextFieldDelegate { func configureMultiAnswerCell(indexPath: IndexPath) -> MultipleAnswerTableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "MultipleAnswerTableViewCell") as! MultipleAnswerTableViewCell cell.parentVC = self return cell } func savedAnswer(answer: SurveyAnswer) { self.surveyAnswers.append(answer) print("answerLo", answer.answer) } // TableView数据源方法 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch cellTypes[indexPath.row] { case .fixedTop: return configureFixedTop(indexPath: indexPath) case .multiAnswer: return configureMultiAnswerCell(indexPath: indexPath) } } // 可疑的保存按钮点击方法 @IBAction func saveButtonTapped(_ sender: UIButton) { let cell = tableView.dequeueReusableCell(withIdentifier: "MultipleAnswerTableViewCell") as! MultipleAnswerTableViewCell cell.mainSaveTapped(stackView: savedStack) } }
问题根源
saveButtonTapped方法的错误操作:调用tableView.dequeueReusableCell会创建一个全新的MultipleAnswerTableViewCell实例,这个新实例从未被赋值parentVC,因此调用mainSaveTapped时,Cell内的parentVC为nil,触发隐式解包错误。- 隐式可选类型的风险:
var parentVC: AddQuestionViewController!使用隐式可选,一旦值为nil就会直接崩溃,即使后续用可选绑定也无法提前规避实例未初始化的问题。 - 强引用的潜在循环风险:直接持有父控制器的强引用,可能引发循环引用导致内存泄漏。
解决方案
方案1:修复保存按钮获取Cell的方式
不要重新创建Cell,而是获取当前TableView中已显示的Cell实例:
@IBAction func saveButtonTapped(_ sender: UIButton) { // 根据cellTypes中multiAnswer的位置,获取对应的indexPath guard let row = cellTypes.firstIndex(of: .multiAnswer) else { return } let indexPath = IndexPath(row: row, section: 0) if let cell = tableView.cellForRow(at: indexPath) as? MultipleAnswerTableViewCell { cell.mainSaveTapped(stackView: savedStack) } }
方案2:修改Cell的父控制器引用为可选类型
将隐式可选改为普通可选,避免强制解包崩溃:
class MultipleAnswerTableViewCell: UITableViewCell, SurveyAnswerViewDelegate, UITextFieldDelegate { // 改为可选类型 var parentVC: AddQuestionViewController? // 调用方法时增加安全判断 guard let text = textField.text, let order = Int(textField.tag), let parentVC = parentVC else { return } let surveyAnswer = SurveyAnswer(answer: text, order: order) surveyAnswers.append(surveyAnswer) parentVC.savedAnswer(answer: surveyAnswer) }
方案3:使用代理模式(推荐)
利用已定义的MultipleAnswerTableViewCellDelegate,避免直接持有父控制器引用,消除循环引用风险:
1. 完善Cell的代理协议
// 确保代理使用weak避免循环引用 protocol MultipleAnswerTableViewCellDelegate: AnyObject { func multipleAnswerCell(_ cell: MultipleAnswerTableViewCell, didSave answer: SurveyAnswer) } class MultipleAnswerTableViewCell: UITableViewCell, SurveyAnswerViewDelegate, UITextFieldDelegate { weak var delegate: MultipleAnswerTableViewCellDelegate? // 触发保存时调用代理方法 guard let text = textField.text, let order = Int(textField.tag) else { return } let surveyAnswer = SurveyAnswer(answer: text, order: order) surveyAnswers.append(surveyAnswer) delegate?.multipleAnswerCell(self, didSave: surveyAnswer) }
2. 父控制器实现代理并绑定
class AddQuestionViewController: BaseViewController, ..., MultipleAnswerTableViewCellDelegate { func configureMultiAnswerCell(indexPath: IndexPath) -> MultipleAnswerTableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "MultipleAnswerTableViewCell") as! MultipleAnswerTableViewCell cell.delegate = self // 替换parentVC赋值为代理绑定 return cell } // 实现代理方法 func multipleAnswerCell(_ cell: MultipleAnswerTableViewCell, didSave answer: SurveyAnswer) { self.surveyAnswers.append(answer) print("answerLo", answer.answer) } }
内容的提问来源于stack exchange,提问作者Mert Köksal
相关产品推荐
相关产品推荐

