Swift中代理方法无法更新UITableView数据的问题排查
问题原因
你现在的核心问题是**TopicViewController和ViewController各自创建了独立的Engine实例**——也就是说两个控制器用的不是同一个Engine对象。当你调用addCourse或addTopic时,触发delegate?.didAddCourse()的是其中一个Engine实例(比如ViewController里的那个),但TopicViewController绑定的是另一个完全独立的Engine,所以它的didAddCourse方法根本不会被调用,表格自然不会更新。
解决方案
方案1:把Engine改成单例(全局唯一实例)
这样所有控制器都共用同一个Engine,代理绑定后就能收到回调。
修改EngineController.swift:
protocol EngineDelegate: AnyObject { func didAddCourse() } class Engine { // 单例实现,全局唯一实例 static let shared = Engine() // 私有化构造方法,防止外部随便创建新实例 private init() {} weak var delegate: EngineDelegate? // 补充你原来的属性(courses数组、索引变量等) var courses: [Course] = [] var requestedCourseIndex: Int = 0 var requestedTopicIndex: Int = 0 func addCourse(name: String, content: String) { let separatedContent = content.components(separatedBy: "\n") courses.append(Course(name: name, description: "" ,topics: [])) for topic in separatedContent { courses[courses.endIndex - 1].topics.append(Course.Topic(subTopics: [], topicName: topic)) } delegate?.didAddCourse() } func addTopic(content: String) { let separatedContent = content.components(separatedBy: "\n") for topic in separatedContent { courses[requestedCourseIndex].topics[requestedTopicIndex].subTopics.append(Course.Topic.SubTopic(subTopicName: topic, subTopicBody: "")) } let topics = courses[requestedCourseIndex].topics[requestedTopicIndex].subTopics.count print(topics) delegate?.didAddCourse() } }
然后修改两个控制器的engine实例,改成用单例:TopicViewController.swift:
import UIKit class TopicViewController: UITableViewController, EngineDelegate { // 不再自己创建,用全局单例 let engine = Engine.shared func didAddCourse() { DispatchQueue.main.async { self.tableView.reloadData() self.stopCellAnimation() } } override func viewDidLoad() { super.viewDidLoad() // 绑定代理 engine.delegate = self } // 确保你的tableView数据源方法用的是engine.courses,比如: override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return engine.courses.count } }
ViewController.swift同理:
import UIKit class ViewController: UITableViewController, EngineDelegate { let engine = Engine.shared override func viewDidLoad() { super.viewDidLoad() engine.delegate = self } func didAddCourse() { DispatchQueue.main.async { self.tableView.reloadData() } } // 数据源方法同样使用engine.courses }
⚠️ 注意:如果两个控制器同时设置engine.delegate = self,最后设置的会覆盖之前的,因为delegate是单个弱引用对象。如果需要两个控制器都收到回调,用下面的多代理方案。
方案2:支持多代理(让多个控制器同时收到回调)
修改EngineController.swift的代理部分,用数组存储多个代理:
protocol EngineDelegate: AnyObject { func didAddCourse() } class Engine { static let shared = Engine() private init() {} // 用数组存储多个代理,弱引用避免循环引用 private var delegates: [WeakDelegate] = [] // 添加代理的方法 func addDelegate(_ delegate: EngineDelegate) { // 避免重复添加同一个代理 if !delegates.contains(where: { $0.delegate === delegate }) { delegates.append(WeakDelegate(delegate: delegate)) } } // 移除代理的方法(比如控制器销毁时调用) func removeDelegate(_ delegate: EngineDelegate) { delegates.removeAll(where: { $0.delegate === delegate }) } // 遍历所有代理,调用方法 private func notifyDidAddCourse() { // 先过滤掉已释放的代理 delegates = delegates.filter { $0.delegate != nil } delegates.forEach { $0.delegate?.didAddCourse() } } // 包装类,用来实现弱引用数组 private class WeakDelegate { weak var delegate: EngineDelegate? init(delegate: EngineDelegate) { self.delegate = delegate } } // 原有方法不变,把delegate?.didAddCourse()换成notifyDidAddCourse() func addCourse(name: String, content: String) { // ...原有代码 notifyDidAddCourse() } func addTopic(content: String) { // ...原有代码 notifyDidAddCourse() } // 你的其他属性 var courses: [Course] = [] var requestedCourseIndex: Int = 0 var requestedTopicIndex: Int = 0 }
然后在控制器里绑定代理:TopicViewController.swift的viewDidLoad:
override func viewDidLoad() { super.viewDidLoad() engine.addDelegate(self) } // 别忘了在控制器销毁时移除代理,避免内存问题 deinit { engine.removeDelegate(self) }
ViewController.swift同样修改即可,这样两个控制器都能收到didAddCourse的回调,各自刷新表格。
内容的提问来源于stack exchange,提问作者Puk
相关产品推荐
相关产品推荐

