Swift中TableViewDataSource分离到其他类的两种写法为何一失效一正常?
嘿,这个问题其实是iOS开发里很常见的ARC内存管理坑,两种写法看似等价,但本质差在对象的生命周期上,咱们一步步说清楚:
第一种写法失效的原因
你看这段代码:
override func viewDidLoad() { super.viewDidLoad(); tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") tableView.dataSource = TableViewDataSource(); }
这里的TableViewDataSource()是在viewDidLoad方法内部创建的局部对象——当viewDidLoad执行完毕后,这个对象没有任何强引用指向它。iOS的ARC(自动引用计数)会立刻销毁这个没有被强引用持有的对象。
而UITableView的dataSource属性是**弱引用(weak)**类型(UIKit里几乎所有delegate/dataSource都是弱引用,目的是避免循环引用),所以当数据源对象被销毁后,tableView的dataSource就变成了nil,自然无法响应numberOfRowsInSection、cellForRowAt这些数据源方法,表视图也就没法正常显示内容了。
第二种写法能正常工作的原因
再看这段代码:
var dataSource:TableViewDataSource!; override func viewDidLoad() { super.viewDidLoad(); tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell") dataSource = TableViewDataSource(); tableView.dataSource = dataSource; }
这里你把dataSource声明为ViewController的存储属性,ViewController本身会对这个数据源对象持有一个强引用。只要ViewController没有被销毁,这个数据源对象就会一直保存在内存中,tableView的弱引用dataSource就能一直指向有效的对象,所以表视图可以正常调用数据源方法,显示内容。
总结一下
核心原则就是:你需要给自定义的数据源对象一个强引用持有者,避免它被ARC提前销毁。把数据源存为控制器的属性是最常规、最稳妥的做法,当然你也可以用其他方式(比如闭包捕获、全局存储等)来保持强引用,但属性方式是最符合iOS开发规范的。
内容的提问来源于stack exchange,提问作者Murad Sofiyev

