注意 此错误已在 iOS 9 中修复,因此到那时整个问题将变得毫无意义。下面的讨论仅适用于它明确适用的特定系统和 Swift 版本。
这显然是一个错误,但也有一个非常简单的解决方案。我会解释问题,然后给出解决方案。请注意,我是为 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 中的错误引起的。你不知道,init(style:) 本身调用init(nibName:bundle:)。它在self 上调用它。那就是你 - MyTableViewController。但是 MyTableViewController 没有实现init(nibName:bundle:)。也不继承 init(nibName:bundle:),因为你已经提供了一个指定的初始化器,从而切断了继承。
您唯一的解决方案是实施 init(nibName:bundle:)。但您不能,因为该实现需要您设置实例属性 greeting - 而您不知道将其设置为什么。
简单的解决方案
简单的解决方案 - 几乎太简单了,这就是为什么很难想到 - 是:不要继承 UITableViewController。为什么这是一个合理的解决方案?因为你从一开始就没有真正需要子类化它。 UITableViewController 基本上是一个毫无意义的类;它不会为你做任何你不能为自己做的事情。
所以,现在我们将把我们的类重写为 UIViewController 子类。我们仍然需要一个表格视图作为我们的视图,所以我们将在loadView 中创建它,并且我们也将它挂在那里。更改标记为带星号的 cmets:
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,我们失去了从情节提要中获取原型单元格的能力。但这没有异议,因为我们从一开始就没有这种能力。请记住,我们的假设是我们正在继承并调用我们自己的子类的初始化程序。如果我们从情节提要中获取原型单元,情节提要将通过调用init(coder:) 来实例化我们,并且从一开始就不会出现问题。