Swift 3 ObjC 可选协议方法未在子类中调用

Swift 3 ObjC Optional Protocol Method Not Called in Subclass

我有以下 class 层次结构:

class ScrollableViewController: UIViewController, UITableViewDelegate { // ... }

实现一个 UITableViewDelegate 协议方法,例如tableView:willDisplayCellAt:

在我的 class SpecificScrollableViewController 中,它继承自 ScrollableViewController,不再调用新的可选协议方法,例如tableView(_:heightForRowAt:)

tl;dr 您需要在函数声明前加上 Objective-C 声明前缀,例如

@objc(tableView:heightForRowAtIndexPath:)
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  // return stuff
}

由于 Swift 3 Migration Guide 指出:

,我被告知这是一个解决方案

If you implement an optional Objective-C protocol requirement in a subclass of a class that declares conformance, you’ll see a warning, “Instance method ‘…’ nearly matches optional requirement ‘…’ of protocol ‘…’”

• Workaround: Add an @objc(objectiveC:name:) attribute before the implementation of the optional requirement with the original Objective-C selector inside.

我相当确定这是一个错误:似乎允许检查选择器功能的运行时动态在 Grand Swift 当协议方法在子类中时重命名时没有得到正确桥接.使用 Objective-C 名称作为函数声明的前缀可以正确地将 Swift 桥接到 Objective-C 并允许 Swift 3 个重命名的方法可以从 [=21] 中使用 canPerformAction:withSender: 查询=]

这似乎已在 Swift 3.0.1 中针对普通子类修复,但未针对通用子类修复:

class A: NSObject, UITableViewDelegate {}

class B: A {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
}

class C<T>: A {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {}
}

print(#selector(B.tableView(_:didSelectRowAt:))) // tableView:didSelectRowAtIndexPath:
print(#selector(C<Int>.tableView(_:didSelectRowAt:))) // tableView:didSelectRowAt:

参见:https://bugs.swift.org/browse/SR-2817

修复它:

@objc(tableView:heightForRowAtIndexPath:)
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  // return stuff
}