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

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

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


我在编辑器中得到一个错误:

无法找到一个超载的'init'接受提供的 参数

当我把selector: test()改为selector: nil时,错误就消失了。

我试过了:

选择器:测试() 选择器:测试 选择器:选择器(测试())

但是什么都没用,我在参考文献中找不到解决方案。


当前回答

创建如下所示的选择器。 1.

UIBarButtonItem(
    title: "Some Title",
    style: UIBarButtonItemStyle.Done,
    target: self,
    action: "flatButtonPressed"
)

2.

flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)

注意,@selector语法消失了,取而代之的是一个简单的String来命名要调用的方法。有一个方面,我们都同意冗长是一个障碍。当然,如果我们声明有一个名为flatButtonPressed的目标方法:我们最好写一个:

func flatButtonPressed(sender: AnyObject) {
  NSLog("flatButtonPressed")
}

设置定时器:

    var timer = NSTimer.scheduledTimerWithTimeInterval(1.0, 
                    target: self, 
                    selector: Selector("flatButtonPressed"), 
                    userInfo: userInfo, 
                    repeats: true)
    let mainLoop = NSRunLoop.mainRunLoop()  //1
    mainLoop.addTimer(timer, forMode: NSDefaultRunLoopMode) //2 this two line is optinal

为了完整,这里是flatButtonPressed

func flatButtonPressed(timer: NSTimer) {
}

其他回答

正如许多人所说,选择器是一种动态调用方法的objective - c方式,已经被带到了Swift中,在某些情况下我们仍然坚持使用它,比如UIKit,可能是因为他们在SwiftUI上工作来取代它,但一些api有更Swift的版本,比如Swift Timer,例如你可以使用

class func scheduledTimer(withTimeInterval interval: TimeInterval, 
                                            repeats: Bool, 
                                              block: @escaping (Timer) -> Void) -> Timer

相反,你可以这样称呼它

Timer.scheduledTimer(withTimeInterval: 1, 
                              repeats: true ) {
    ... your test code here
}

or

Timer.scheduledTimer(withTimeInterval: 1, 
                              repeats: true,
                              block: test)

方法test需要一个Timer参数,或者如果你想test需要一个命名参数

Timer.scheduledTimer(withTimeInterval: 1, 
                              repeats: true,
                              block: test(timer:))

你也应该使用Timer而不是NSTimer因为NSTimer是objective-c的旧名字

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表达式从Swift函数类型中构造一个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()指令的改进,因为编译器的- wundeclated -selector检查只验证命名的选择器是否存在。你传递给#selector的Swift函数引用检查是否存在,类中的成员和类型签名。)

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

Multiple functions with the same base name can be differentiated by their parameter labels using the aforementioned syntax for function references (e.g. insertSubview(_:at:) vs insertSubview(_:aboveSubview:)). But if a function has no parameters, the only way to disambiguate it is to use an as cast with the function's type signature (e.g. foo as () -> () vs foo(_:)). There's a special syntax for property getter/setter pairs in Swift 3.0+. For example, given a var foo: Int, you can use #selector(getter: MyClass.foo) or #selector(setter: MyClass.foo).

将军指出:

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

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

记住,私有符号也不会向运行时公开——您的方法至少需要具有内部可见性。

键路径:它们与选择器相关,但并不完全相同。在Swift 3中也有一个特殊的语法:例如chris.valueForKeyPath(#keyPath(Person.friends.firstName))。具体请参见SE-0062。在Swift 4中甚至有更多的KeyPath内容,所以请确保您使用了正确的基于KeyPath的API,而不是选择器。

你可以在Using Swift with Cocoa和Objective-C中阅读更多关于与Objective-C api交互的选择器。

注意:在Swift 2.2之前,选择器符合StringLiteralConvertible,所以你可能会发现旧代码中裸字符串被传递给接受选择器的api。你需要在Xcode中运行“Convert to Current Swift Syntax”,使用#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中添加了一个手势识别器到UILabel中,所以在阅读以上所有内容后,我发现以下是对我有用的:

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: "labelTapped:")

其中“Selector”被声明为:

func labelTapped(sender: UILabel) { }

请注意,它是公共的,我没有使用Selector()语法,但也可以这样做。

let tapRecognizer = UITapGestureRecognizer(
            target: self,
            action: Selector("labelTapped:"))

使用#selector将在编译时检查代码,以确保要调用的方法确实存在。更好的是,如果方法不存在,你会得到一个编译错误:Xcode将拒绝构建你的应用,从而消除另一个可能的错误来源。

override func viewDidLoad() {
        super.viewDidLoad()

        navigationItem.rightBarButtonItem =
            UIBarButtonItem(barButtonSystemItem: .Add, target: self,
                            action: #selector(addNewFireflyRefernce))
    }

    func addNewFireflyReference() {
        gratuitousReferences.append("Curse your sudden but inevitable betrayal!")
    }