ios - 如何在运行时通过 Selector 在 Swift 中调用泛型函数?

标签 ios swift generics generic-function

我正在尝试像这样在运行时为 UI 事件注册回调。

func observeEvent(event: UIControlEvent){
    self.addTarget(self, action: "eventFired:", forControlEvents: event)
}

func eventFired<T>(sender: T){
    print("event fired!")
}

它崩溃并说它在运行时找不到 eventFired:。 你能帮忙解决这个问题吗?

最佳答案

您不能使用通用函数作为选择器字符串文字的目标。为什么?选择器不包含有关其目标参数类型的信息,仅包含:

  • 选择器目标的名称(方法名称),
  • 和(隐含地)选择器目标的参数数量

因此,Swift 甚至不允许将具有相同名称和相同参数数量的非泛型函数用作选择器的目标,因为这会与 w.r.t. 发生冲突。选择器应该使用哪个目标。


例如,考虑以下( Playground )示例:

class Foo : UIView {
    var button = UIButton()

    init() {
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        button.addTarget(self, action: "eventFired:", forControlEvents: UIControlEvents.TouchUpInside)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func eventFired(sender: UIButton){
        print("event fired!")
    }
}

let foo = Foo()
foo.button.sendActionsForControlEvents(.TouchUpInside)
    /* event fired! */

这按预期工作,但现在尝试将以下方法添加到 Foo 类:

class Foo : UIView { 

    // ...

    func eventFired(nothingToDoWithAButton: Int){
        print("another event fired!")
    }
}

与上面相同的示例现在会产生错误,因为选择器在调用哪个目标方法方面存在冲突。

let foo = Foo()
foo.button.sendActionsForControlEvents(.TouchUpInside)
    /* error ... */

error: method 'eventFired' with Objective-C selector 'eventFired:' conflicts with previous declaration with the same Objective-C selector ...

我们最后注意到,如果我们把上面的附加方法改成

class Foo : UIView { 

    // ...

    func eventFired(nothingToDoWithAButton: Int, someBool: Bool){
        print("another event fired!")
    }
}

然后选择器按预期找到它的目标 eventFired(:UIButton),因为由于 number 的不同,两个 eventFired 方法可能不同论点


从上面可以看出,选择器/选择器字面量在当前形式下显然太原始,无法明确地使用和识别通用目标。选择器需要包含目标参数类型知识,以便能够区分同名/相同参数目标。

最后请注意,选择器以其当前形式作为字符串文字,将在 Swift 2.2 中弃用,并在 Swift 3.0 中删除,以支持新的表达式语法 #selector,请参阅:


(编辑添加 w.r.t. OP:s 下面的评论)

您可以尝试通过让目标捕获来自引用类型 (...(:AnyObject)) 的各种信号来模仿某种通用的选择器目标,并根据引用类型处理事件包装在 AnyObject 参数中:

class Foo : UIView {
    var button = UIButton()
    var sw = UISwitch()

    init() {
        super.init(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        button.addTarget(self, action: "eventFired:", forControlEvents: .TouchUpInside)
        sw.addTarget(self, action: "eventFired:", forControlEvents: .ValueChanged)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func eventFired(sender: AnyObject){
        switch(sender) {
        case is UIButton: print("button event fired!")
        case is UISwitch: print("switch event fired!")
        case _: print("unknown event fired")
        }
    }
}

let foo = Foo()
foo.button.sendActionsForControlEvents(.TouchUpInside)
    /* button event fired! */
foo.sw.sendActionsForControlEvents(.ValueChanged)
    /* switch event fired! */

关于ios - 如何在运行时通过 Selector 在 Swift 中调用泛型函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35847193/

相关文章:

c# - 强制 C# 转换为泛型

ios - 使用 Swift 在 iOS 上无限滚动

ios - CBPeripheralManager 未上电

ios - 在 Firebase 中更改结构有多物理

ios - PrototypeCell 上的空白空间

ios - ' fatal error : unexpectedly found nil while unwrapping an Optional value' for UIToolbar

C# 通用运算符 - RTTI 方法

Java boolean addAll 方法和泛型类型参数问题

ios - 有什么方法可以创建自定义 VoiceOver 手势吗?

swift - CommandLine.arguments 和 Processinfo.processinfo.arguments 在 swift 中的区别