注意:此问题已在iOS 9中得到解决,因此此事将毫无意义。以下讨论仅适用于特定系统和版本的Swift。
显然,这是一个bug,但是也有一个非常简单的解决方案。我会先解释问题,然后给出解决方案。请注意,我是针对Xcode 6.3.2和Swift 1.2编写的;自从Swift首次推出以来,Apple一直在这方面混乱不堪,所以其他版本的行为会有所不同。
存在的理由
您将通过手动实例化UITableViewController(即通过在代码中调用其初始化器)来进行操作。并且您想要子类化UITableViewController,因为您有要赋予它的实例属性。
问题
因此,您首先拥有一个实例属性:
class MyTableViewController: UITableViewController {
let greeting : String
}
没有默认值,因此您必须编写初始化程序:
class MyTableViewController: UITableViewController {
let greeting : String
init(greeting:String) {
self.greeting = greeting
}
}
但这不是一个合法的初始化程序 - 你必须调用super
。假设你对super
的调用是调用init(style:)
。
class MyTableViewController: UITableViewController {
let greeting : String
init(greeting:String) {
self.greeting = greeting
super.init(style: .Plain)
}
}
但是你仍然无法编译,因为你需要实现init(coder:)
方法。因此,你可以这样做:
class MyTableViewController: UITableViewController {
let greeting : String
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
init(greeting:String) {
self.greeting = greeting
super.init(style: .Plain)
}
}
您的代码现在已经可以编译了!您现在可以通过调用您编写的初始化器实例化这个表视图控制器子类,感到非常高兴(您认为是这样):
let tvc = MyTableViewController(greeting:"Hello there")
一切看起来都很美好,直到你运行这个应用程序,此时你会遇到崩溃并显示以下信息:
致命错误:未实现初始化器init(nibName:bundle :)
崩溃的原因以及你为什么无法解决它
崩溃是由Cocoa中的一个bug引起的。你不知道的是,init(style :)
本身调用了init(nibName:bundle :)
。而它在self
上调用。也就是说,这是你-MyTableViewController。但是MyTableViewController没有实现init(nibName:bundle:)
。而且也没有继承init(nibName:bundle:)
,因为你已经提供了指定的初始化器,从而断开了继承。
你唯一的解决方案是实现init(nibName:bundle:)
。但你不能这样做,因为那样的实现将要求你设置实例属性greeting
,而你不知道该设置为什么。
简单的解决方案
简单的解决方案 - 几乎太简单了,这就是为什么它很难想到 - 是:不要子类化UITableViewController。为什么这是一个合理的解决方案?因为你实际上从来没有需要子类化它。UITableViewController是一个基本上没用的类;它为你做的事情都可以自己完成。
所以,现在我们将把我们的类重写为UIViewController的子类。我们仍然需要作为我们的视图的表视图,所以我们将在loadView
中创建它,并在那里连接它。更改标记为星号注释:
class MyViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let greeting : String
weak var tableView : UITableView!
init(greeting:String) {
self.greeting = greeting
super.init(nibName:nil, bundle:nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
self.view = UITableView(frame: CGRectZero, style: .Plain)
self.tableView = self.view as! UITableView
self.tableView.delegate = self
self.tableView.dataSource = self
}
}
当然,您还需要添加最小必需的数据源方法。我们现在这样实例化我们的类:
let tvc = MyViewController(greeting:"Hello there")
我们的项目编译并运行得很顺利。问题解决了!
一个反驳 - 不是
你可能会认为,通过不使用UITableViewController,我们失去了从Storyboard获取原型单元格的能力。但这不是反驳,因为我们一开始就从未有过这样的能力。请记住,我们的假设是我们正在子类化并调用自己子类的初始化程序。如果我们从Storyboard获取原型单元格,Storyboard将通过调用 init(coder:)
实例化我们,而问题根本不会出现。