为什么在Swift中通过Selector调用函数时不遵循默认参数?

5

通过选择器调用的方法不会遵循默认参数值。

示例

如果我有一个按钮进行了连接,通过选择器调用一个函数:

func setupButton() {
    self.button.addTarget(self, action: #selector(printValue), for: .touchUpInside)
}

@objc func printValue(value:Int = 7)
{
    if value == 7 {
       print("received default")
    } else {
       print("default unused")
    }
}

当通过代码调用此方法时,会打印出“接收的默认值”,但当我按下按钮并通过选择器调用该方法时,会打印出“默认未使用”,因为7没有作为值设置。
为什么会发生这种情况?
1个回答

10

当你调用函数时,Swift 编译器会自动插入默认参数。
所以这是一个编译时的事情。

通过选择器手动调用函数时,您使用的是 Objective-C 运行时,它对您的默认参数一无所知。
这是运行时的事情。

此外,在 Objective-C 中不存在默认参数。

您可以将 Swift 默认参数视为编译时的便利。
但是一旦运行代码,它们基本上就消失了。

编辑

如果您正在寻找解决方法,我建议您使用两个不带默认参数的不同函数:

@objc func printValue()
{
    printValue(value: 7)
}

@objc func printValue(value:Int)
{}

这样,您可以通过Objective-C运行时无需参数调用它。 请注意,使用#selector时,您需要进行强制转换以解决歧义:

self.button?.addTarget(self, action: #selector((printValue as () -> Void)), for: .touchUpInside)

谢谢。有没有其他地方记录了这样的怪癖(Swift 与 Obj-C 运行时能力之间预期行为冲突)? - Dan Carlson
@DanCarlson,很遗憾我不知道有这样的文档。如果你在寻找解决方法,请参考编辑内容。 - Macmade
为了学习的目的,您建议的“变通方法”在这种情况下为什么不被Swift编译器自动执行呢?也就是说,如果一个方法有默认参数值,那么编译器是否可以欺骗该函数的名称,并在调用原始函数之前插入默认值来创建一个包装器函数? - Dan Carlson
1
@DanCarlson 这可能对 Swift 编译器来说要求过高了。它不能真正检测您将如何使用选择器。在您的示例中,您没有获得预期的行为,但您也可以使用相同的选择器和 NSInvocation,自己传递所需的参数。那样也是可以的。另外,我认为自动创建“包装器”函数可能会导致不必要的副作用。 - Macmade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接