问题描述
给出以下内容.
protocol EntityType {
var displayString: String { get }
}
extension String: EntityType {
var displayString: String { return self }
}
class GenericListViewController<Entity>: UIViewController, UITableViewDataSource, UITableViewDelegate where Entity: EntityType {
let items: [Entity]
let tableView: UITableView
init(items: [Entity]) {
self.items = items
self.tableView = UITableView()
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor)
])
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
tableView.dataSource = self
tableView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.text = items[indexPath.row].displayString
return cell
}
// func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//
// }
}
class StringListViewController: GenericListViewController<String> {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("selected: \(items[indexPath.row])")
}
}
为什么在具体的子类中不调用tableView:didSelectRowAt:
?对于非泛型类型或父类具有实现并且子类覆盖它的情况,这可以很好地工作.这是预期的行为,Swift错误还是我遗漏了一些东西?
Why isn't tableView:didSelectRowAt:
being called in the concrete subclass? This works fine for non-generic types or when the parent class has an implementation and the sub-class overrides it. Is this expected behavior, Swift bug or am I missing something?
推荐答案
这似乎是一个可能的错误.我猜想这可能与Swift泛型对Objective-C运行时不可见有关,因此即使调用直接在GenericListViewController<Entity>
中实现的方法,Swift之间也可能会出现一些错误行为和Obj-C运行时试图找出覆盖.绝对值得一个错误报告.
It does seem like a possible bug. My guess it that it might have something to do with the fact that Swift generics are not visible to the Objective-C runtime, so even though your methods implemented directly in GenericListViewController<Entity>
are being called, there may be some buggy behavior between the Swift and Obj-C runtimes trying to figure out overrides. Definitely worth a bug report.
我会注意到,在严格的OOP中,抽象超类通常不符合协议本身,它们只是提供默认的实现.声明协议一致性并填写任何缺少的实现仍取决于具体的子类.
I will note though, that in strict OOP abstract superclasses generally do not conform to protocols themselves, they simply provide default implementations. It's still up to the concrete subclasses to declare protocol conformance and fill in any missing implementations.
对于上面的代码,您的GenericListViewController<Entity>
类不应符合UITableViewDataSource
或UITableViewDelegate
.它只是提供了默认方法实现,该默认方法实现将允许具体的子类遵循而不必重写那些方法实现(除非需要重写).您的StringListViewController
应该是声明符合这两个协议的一个.如果您修改代码来执行此操作,则它实际上将按预期工作.
In the case of your code above, your GenericListViewController<Entity>
class should not conform to UITableViewDataSource
or UITableViewDelegate
. It simply provides the default method implementations that would allow a concrete subclass to conform without having to rewrite those method implementations (unless override is desired). Your StringListViewController
should be the one declaring conformance to the two protocols. If you modify your code to do that, it will actually work as expected.
这不会改变您可能已经在Swift/Obj-C interop中发现一个错误的事实.我相信您目前应该工作正常,尽管这不是处理协议一致性的严格OOP方法.
This doesn't change the fact that you have probably discovered a bug in Swift / Obj-C interop, though. I believe that what you have currently should work, although it's not the strict OOP way of handling protocol conformance.
这篇关于泛型类不会将委托调用转发给具体的子类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!