在 Swift 中,如何在不实际调用该方法的情况下测试对象是否实现了签名不同的可选协议方法?

In Swift, how can you test if an object implements an optional protocol method which differs by signature without actually calling that method?

使用Swift,是否可以在不实际调用该方法的情况下测试对象是否实现了可选协议方法?这适用于可选方法仅在其签名上不同的情况。

考虑这段代码...

@objc public protocol TestDelegate : AnyObject {
    @objc optional func testx()
    @objc optional func test(with string:String)
    @objc optional func test(with2 int:Int)
}

let delegate:TestDelegate? = nil

if let _ = delegate?.test(with:) {
    print("supports 'test(with:)'")
}

if let _ = delegate?.testx {
    print("supports 'testx'")
}

如果将以上内容粘贴到 playground 中,它会按预期工作。

但是,如果将 testx 更改为 test,它将不再有效。

同样,如果您将 test(with2) 更改为 test(with),那也不起作用。

有什么方法可以测试那些仅签名不同的方法吗?

嘿 MarqueIV 检查可选的你可以使用内置函数

func responds(to aSelector: Selector!) -> Bool

Returns一个布尔值,表示接收方是否实现或继承了可以响应指定消息的方法。

应用程序负责确定错误响应是否应被视为错误。

您无法通过使用 super 关键字向对象发送 responds(to:) 来测试对象是否从其 superclass 继承方法。

此方法仍将测试整个对象,而不仅仅是 superclass 的实现。

因此,发送responds(to:)给super相当于发送给self。

相反,您必须直接在对象的 superclass 上调用 NSObject class 方法 instanceRespond(to:),如以下代码片段所示。

清单 1

if( [MySuperclass instancesRespondToSelector:@selector(aMethod)] ) {
    // invoke the inherited method
    [super aMethod];
}

您不能简单地使用 [[self superclass] instanceRespondToSelector:@selector(aMethod)] 因为如果它被子 class.

请注意,如果接收方能够将 aSelector 消息转发给另一个对象,它将能够响应该消息,尽管是间接的,即使此方法 returns 为 false。 参数
一个选择器
标识消息的选择器。 Returns 如果接收器实现或继承可以响应选择器的方法,则为 true,否则为 false。 SDK iOS 2.0+、macOS 10.0+、tvOS 9.0+、watchOS 2.0+

中所示,您可以显式强制将函数引用强制为其预期类型,以解决此类歧义。

唯一的区别是,由于此类函数引用是通过可选链接完成的 @optional 协议要求,因此您需要强制转换为函数的可选类型。从那里,您可以与 nil 进行比较,以确定委托是否为非零,并且是否实现了给定的要求。

例如:

import Foundation

@objc public protocol TestDelegate : AnyObject {
  @objc optional func test()

  // Need to ensure the requirements have different selectors.
  @objc(testWithString:) optional func test(with string: String)
  @objc(testWithInt:) optional func test(with int: Int)
}

class C : TestDelegate {
  func test() {}
  func test(with someString: String) {}
  func test(with someInt: Int) {}
}

var delegate: TestDelegate? = C()

if delegate?.test as (() -> Void)? != nil {
  print("supports 'test'")
}

if delegate?.test(with:) as ((String) -> Void)? != nil {
  print("supports 'test w/ String'")
}

if delegate?.test(with:) as ((Int) -> Void)? != nil {
  print("supports 'test w/ Int'")
}

// supports 'test'
// supports 'test w/ String'
// supports 'test w/ Int'

请注意,我已经为 test(with:) 要求提供了唯一的选择器,以确保它们不会冲突(这不会影响消歧,只允许 class C符合 TestDelegate).