@selector()在Swift中?

我试图在Swift创建一个NSTimer ,但我遇到了一些麻烦。

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)

test()是同一个类中的一个函数。


编辑器中出现错误:

无法找到接受提供的参数的'init'的重载

当我将selector: test()更改为selector: nil错误消失。

我试过了:

  • selector: test()
  • selector: test
  • selector: Selector(test())
  • 但没有任何工作,我无法在参考文献中找到解决方案。


    Swift本身并不使用选择器 - Objective-C中使用选择器的几种设计模式在Swift中的工作方式不同。 (例如,使用任选的链上的协议类型或is / as测试来代替respondsToSelector:和使用闭随时随地可以代替performSelector:用于更好的类型/存储器安全)

    但是仍然有许多重要的基于ObjC的API使用选择器,包括定时器和目标/动作模式。 Swift提供了用于处理这些类型的Selector类型。 (Swift自动使用它来代替ObjC的SEL类型。)

    在Swift 2.2(Xcode 7.3)及更高版本(包括Swift 3 / Xcode 8和Swift 4 / Xcode 9)中:

    您可以构建一个Selector从使用一个斯威夫特功能型#selector表达。

    let timer = Timer(timeInterval: 1, target: object,
                      selector: #selector(MyClass.test),
                      userInfo: nil, repeats: false)
    button.addTarget(object, action: #selector(MyClass.buttonTapped),
                     for: .touchUpInside)
    view.perform(#selector(UIView.insertSubview(_:aboveSubview:)),
                 with: button, with: otherButton)
    

    这种方法的好处是什么? Swift编译器检查函数引用,因此只能将#selector表达式用于实际存在且可用作选择器的类/方法对(请参见下面的“选择器可用性”)。 您也可以根据需要按照特定功能类型命名的Swift 2.2+规则自由创建函数引用。

    (这实际上是对ObjC的@selector()指令的改进,因为编译器的-Wundeclared-selector检查只验证指定的选择器是否存在。传递给#selector的Swift函数引用检查是否存在,类中的成员资格以及类型签名。)

    对于传递给#selector表达式的函数引用有一些额外的注意事项:

  • 具有相同基本名称的多个函数可以通过参数标签使用前面提到的函数引用语法(例如insertSubview(_:at:) vs insertSubview(_:aboveSubview:) )来insertSubview(_:aboveSubview:) 。 但是,如果一个函数没有参数,只有这样,才能消除歧义它是使用as投用函数的类型签名(例如foo as () -> () VS foo(_:) )。
  • 在Swift 3.0+中,属性getter / setter对有一个特殊的语法。 例如,给定一个var foo: Int ,你可以使用#selector(getter: MyClass.foo)#selector(setter: MyClass.foo)
  • 一般注意事项:

    #selector不起作用的情况,以及命名:有时,您没有使用选择器的函数引用(例如,在ObjC运行时中动态注册的方法)。 在这种情况下,你可以从一个字符串构造一个Selector :例如Selector("dynamicMethod:") - 尽管你失去了编译器的有效性检查。 当你这样做,你需要遵循ObjC的命名规则,包括冒号( : )每个参数。

    选择器可用性:选择器引用的方法必须暴露于ObjC运行时。 在Swift 4中,暴露给ObjC的每个方法都必须以@objc属性作为@objc 。 (在以前的版本中,在某些情况下您可以免费获得该属性,但现在必须明确声明它。)

    请记住, private符号也不会暴露给运行时 - 您的方法至少需要internal可见性。

    关键路径:这些与选择器有关但不完全相同。 在Swift 3中也有一些特殊的语法:例如chris.valueForKeyPath(#keyPath(Person.friends.firstName)) 。 详情请参阅SE-0062。 在Swift 4中还有更多KeyPath东西,所以确保你使用了正确的基于KeyPath的API而不是选择器(如果适用的话)。

    您可以在使用Cocoa和Objective-C的Swift中阅读更多关于与Objective-C API交互的选择器。

    注意:在Swift 2.2之前, Selector符合StringLiteralConvertible ,因此您可能会发现旧的代码将裸露的字符串传递给采用选择器的API。 您需要在Xcode中运行“转换为当前Swift语法”以使用#selector


    这里有一个关于如何在Swift上使用Selector类的简单例子:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
        self.navigationItem.rightBarButtonItem = rightButton
    }
    
    func method() {
        // Something cool here   
    }
    

    请注意,如果以字符串形式传递的方法不起作用,它将在运行时失败,而不是编译时间,并且会导致应用程序崩溃。 小心


    另外,如果您的(Swift)类不从Objective-C类下降,那么您必须在目标方法名称字符串的末尾添加冒号,并且您必须在目标方法中使用@objc属性,例如

    var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
    
    @objc func method() {
        // Something cool here   
    } 
    

    否则在运行时会出现“无法识别的选择器”错误。

    链接地址: http://www.djcxy.com/p/56485.html

    上一篇: @selector() in Swift?

    下一篇: #ifdef replacement in the Swift language