我想子类化 UITableViewController 并能够通过调用不带参数的默认初始化程序来实例化它。
class TestViewController: UITableViewController {
convenience init() {
self.init(style: UITableViewStyle.Plain)
}
}
从 Xcode 6 Beta 5 开始,上面的示例不再有效。
Overriding declaration requires an 'override' keyword
Invalid redeclaration of 'init()'
最佳答案
注意 此错误已在 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)
}
}
您的代码现在可以编译了!您现在很高兴(您认为)通过调用您编写的初始化程序来实例化此 TableView Controller 子类:
let tvc = MyTableViewController(greeting:"Hello there")
在您运行应用程序之前,一切看起来都很愉快,此时您崩溃并显示以下消息:
fatal error: use of unimplemented initializer
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 子类。我们仍然需要一个 TableView 作为我们的 View ,所以我们将在 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 中获取原型(prototype)单元格的能力。但这并不反对,因为我们从一开始就没有这种能力。请记住,我们的假设是我们正在子类化并调用我们自己的子类的初始化程序。如果我们从 Storyboard 中获取原型(prototype)单元, Storyboard 将通过调用 init(coder:)
来实例化我们,问题一开始就不会出现。
关于ios - 如何在 Swift 中继承 UITableViewController,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25139494/