Swift中的@selector()?

Arb*_*tur 647 selector nstimer swift

我正在尝试创建一个NSTimerin Swift但我遇到了一些麻烦.

NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)
Run Code Online (Sandbox Code Playgroud)

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


我在编辑器中收到错误:

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

当我改变selector: test()selector: nil错误消失.

我试过了:

  • selector: test()
  • selector: test
  • selector: Selector(test())

但没有任何作用,我在参考文献中找不到解决方案.

ric*_*ter 920

Swift 本身不使用选择器 - 在Objective-C中使用选择器的几种设计模式在Swift中的工作方式不同.(例如,在协议类型或is/ astests 上使用可选链接代替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表达式从Swift函数类型构造一个.

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)
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 具有相同基本名称的多个函数可以通过其参数标签使用上述函数引用语法(例如insertSubview(_:at:)vs insertSubview(_:aboveSubview:))来区分.但是如果函数没有参数,则消除歧义的唯一方法是使用as具有函数类型签名的强制转换(例如foo as () -> ()vs foo(_:)).
  • Swift 3.0+中的属性getter/setter对有一个特殊的语法.例如,给定a var foo: Int,您可以使用#selector(getter: MyClass.foo)#selector(setter: MyClass.foo).

一般注意事项:

情况下#selector不能正常工作,并命名:有时候你没有一个函数的引用,使一个选择器(例如,用在ObjC运行时动态注册的方法).在这种情况下,您可以Selector从字符串构造一个:例如Selector("dynamicMethod:")- 虽然您丢失了编译器的有效性检查.执行此操作时,您需要遵循ObjC命名规则,包括:每个参数的冒号().

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

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

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

您可以在使用Swift with Cocoa和Objective-C的" 与Objective-C API交互"下阅读有关选择器的更多信息.

注意:Selector符合Swift 2.2之前StringLiteralConvertible,您可能会发现旧代码将裸字符串传递给采用选择器的API.您将要在Xcode中运行"转换为当前Swift语法"以获取使用它们#selector.

  • 这应该可以提到,如果选择器需要参数,那么选择器最后需要一个":".(例如test() - >"test"&test(this:String) - >"test:") (10认同)
  • 使用函数名称的字符串,NSSelectorFromString()也可以工作. (8认同)
  • 我想提一下,虽然"与Objective-C API交互"是在网站上,但它并不在"The Swift Programming Language"一书中. (7认同)
  • 无论如何,在将"selector"作为字符串传递时添加验证?当我们拼写错误等时,IE编译器会警告我们 (6认同)
  • 还应该指出,Cocoa框架期望一个Objective-C样式的方法名称.如果你的方法接受一个参数,你将需要一个':',如果它需要2个参数,`size:andShape:`,如果第一个参数被命名,你可能需要`With`,即`initWithData:`for`func init(数据数据:NSData)` (2认同)
  • 我必须补充说,应该声明选择器函数而不使用**private**修饰符. (2认同)
  • 如何在结构中使用选择器。它给我的错误是“@objc 只能与类的成员、@objc 协议和类的具体扩展一起使用” (2认同)

Osc*_*ros 84

这是一个关于如何Selector在Swift 上使用该类的快速示例:

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   
}
Run Code Online (Sandbox Code Playgroud)

请注意,如果作为字符串传递的方法不起作用,它将在运行时失败,而不是编译时间,并使应用程序崩溃.小心

  • 这太可怕了......有没有"NSStringFromSelector"类型的东西? (13认同)
  • 我们无法相信他们为未经检查的选择器而设计,因为objc有这个 (11认同)
  • @malcomhall:`@ selector`很方便,但它并没有像你想象的那样正式强制执行."未声明的选择器"仅仅是编译器的警告,因为始终可以在运行时引入新的选择器.然而,Swift中的可验证/可重构选择器引用将是[一个很好的特征请求](http://bugreport.apple.com). (4认同)
  • 这个答案很有帮助,但下面的@objc答案更合适. (2认同)

小智 46

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

var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))

@objc func method() {
    // Something cool here   
} 
Run Code Online (Sandbox Code Playgroud)

否则您将在运行时收到"无法识别的选择器"错误.

  • 1.具有冒号的选择器必须采用参数2.根据Apple docs计时器的动作应该采用NSTimer参数3.`Selector`关键字不是强制性的.所以在这种情况下签名必须是`@objc func方法(timer:NSTimer){/*code*/} (3认同)

Kyl*_*egg 31

Swift 2.2+和Swift 3更新

使用新#selector表达式,这消除了使用字符串文字的需要,使得使用不易出错.以供参考:

Selector("keyboardDidHide:")
Run Code Online (Sandbox Code Playgroud)

#selector(keyboardDidHide(_:))
Run Code Online (Sandbox Code Playgroud)

另见:Swift Evolution Proposal

注意(Swift 4.0):

如果使用#selector,则需要将该功能标记为@objc

例:

@objc func something(_ sender: UIButton)


Rob*_*ers 23

对于未来的读者,我发现我遇到了一个问题,并且因为unrecognised selector sent to instance将目标标记func为私有而导致错误.

func 必须公开显示通过与到选择的参考对象被调用.

  • 它没有**公开你仍然可以保持方法私有,但在声明之前添加`objc`.例如:`@objc private func foo(){...`然后你可以使用``foo"`作为你喜欢的选择器 (13认同)

小智 22

Swift 4.0

你创建如下所示的选择器.

1.将事件添加到如下按钮:

button.addTarget(self, action: #selector(clickedButton(sender:)), for: UIControlEvents.touchUpInside)
Run Code Online (Sandbox Code Playgroud)

功能如下:

@objc func clickedButton(sender: AnyObject) {

}
Run Code Online (Sandbox Code Playgroud)

  • 你忘了把`@objc`放在Swift 4中需要的`func`之前. (3认同)

Mar*_*res 19

为了防止其他人遇到与NSTimer相同的问题,其中没有其他答案解决了这个问题,非常重要的是,如果你使用的是一个不直接或深层次地从NSObject继承的类(例如,手动创建swift文件),即使指定如下,其他任何答案都不会起作用:

let timer = NSTimer(timeInterval: 1, target: self, selector: "test", 
                    userInfo: nil, repeats: false)
func test () {}
Run Code Online (Sandbox Code Playgroud)

除了让类继承自NSObject之外,我没有改变任何其他东西,我停止了"无法识别的选择器"错误并使我的逻辑按预期工作.


Sco*_*ter 14

如果您想将参数从NSTimer传递给函数,那么这是您的解决方案:

var somethingToPass = "It worked"

let timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "tester:", userInfo: somethingToPass, repeats: false)

func tester(timer: NSTimer)
{
    let theStringToPrint = timer.userInfo as String
    println(theStringToPrint)
}
Run Code Online (Sandbox Code Playgroud)

在选择器文本(tester :)中包含冒号,您的参数在userInfo中.

您的函数应该将NSTimer作为参数.然后只需提取userInfo即可获取传递的参数.

  • 我正在使用NSTimer(0.01,target:self,...)这不起作用,而使用NSTimer.scheduledTimerWithTimeInterval(0.01,..)DID工作!?奇怪,但感谢@Scooter为您解答! (3认同)

Jon*_*ros 10

选择器是Objective-C中方法名称的内部表示.在Objective-C中,"@ selector(methodName)"会将源代码方法转换为SEL的数据类型.由于您无法在Swift中使用@selector语法(rickster就在那里),您必须直接手动将方法名称指定为String对象,或者将String对象传递给Selector类型.这是一个例子:

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:"logout"
)
Run Code Online (Sandbox Code Playgroud)

要么

var rightBarButton = UIBarButtonItem(
    title: "Logout", 
    style: UIBarButtonItemStyle.Plain, 
    target: self, 
    action:Selector("logout")
)
Run Code Online (Sandbox Code Playgroud)


Kru*_*nal 8

Swift 4.1
带有轻拍手势的样本

let gestureRecognizer = UITapGestureRecognizer()
self.view.addGestureRecognizer(gestureRecognizer)
gestureRecognizer.addTarget(self, action: #selector(self.dismiss(completion:)))

// Use destination 'Class Name' directly, if you selector (function) is not in same class.
//gestureRecognizer.addTarget(self, action: #selector(DestinationClass.dismiss(completion:)))


@objc func dismiss(completion: (() -> Void)?) {
      self.dismiss(animated: true, completion: completion)
}
Run Code Online (Sandbox Code Playgroud)

有关更多详细信息,请参阅Apple的文档:选择器表达式

  • 在 swift 4 中调用选择器是不同的。在 swift 4 中尝试这些答案并查看。如果不进行编辑,这些都不会起作用。在未确保其重要性的情况下,请勿将任何声明标记为垃圾邮件 (2认同)
  • 您必须将objc标记添加到Swift 4的任何选择器.这是正确的答案.你不应该编辑其他人的答案来改变他们的意思.@Krunal完全正确. (2认同)

Joh*_*Lee 6

// for swift 2.2
// version 1
buttton.addTarget(self, action: #selector(ViewController.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(ViewController.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 2
buttton.addTarget(self, action: #selector(self.tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(self.tappedButton2(_:)), forControlEvents: .TouchUpInside)

// version 3
buttton.addTarget(self, action: #selector(tappedButton), forControlEvents: .TouchUpInside)
buttton.addTarget(self, action: #selector(tappedButton2(_:)), forControlEvents: .TouchUpInside)

func tappedButton() {
  print("tapped")
}

func tappedButton2(sender: UIButton) {
  print("tapped 2")
}

// swift 3.x
button.addTarget(self, action: #selector(tappedButton(_:)), for: .touchUpInside)

func tappedButton(_ sender: UIButton) {
  // tapped
}

button.addTarget(self, action: #selector(tappedButton(_:_:)), for: .touchUpInside)

func tappedButton(_ sender: UIButton, _ event: UIEvent) {
  // tapped
}
Run Code Online (Sandbox Code Playgroud)


yoA*_*ex5 6

Objective-C 选择器

选择器标识一个方法。

//Compile time
SEL selector = @selector(foo);

//Runtime
SEL selector = NSSelectorFromString(@"foo");
Run Code Online (Sandbox Code Playgroud)

例如

[object sayHello:@"Hello World"];
//sayHello: is a selector
Run Code Online (Sandbox Code Playgroud)

selector是来自 world 的一个词Objective-C,您可以使用它 fromSwift来调用Objective-CfromSwift 它允许您在运行时执行一些代码

之前的Swift 2.2语法是:

Selector("foo:")
Run Code Online (Sandbox Code Playgroud)

Selector由于函数名称作为参数传递String(“foo”),因此不可能在编译时检查名称。结果你可能会得到一个运行时错误:

unrecognized selector sent to instance
Run Code Online (Sandbox Code Playgroud)

之后的Swift 2.2+语法是:

#selector(foo(_:))
Run Code Online (Sandbox Code Playgroud)

Xcode 的自动完成功能可帮助您调用正确的方法


Ren*_*iya 5

Create Refresh control using Selector method.   
    var refreshCntrl : UIRefreshControl!
    refreshCntrl = UIRefreshControl()
    refreshCntrl.tintColor = UIColor.whiteColor()
    refreshCntrl.attributedTitle = NSAttributedString(string: "Please Wait...")
    refreshCntrl.addTarget(self, action:"refreshControlValueChanged", forControlEvents: UIControlEvents.ValueChanged)
    atableView.addSubview(refreshCntrl)
Run Code Online (Sandbox Code Playgroud)

//刷新控制方法

func refreshControlValueChanged(){
    atableView.reloadData()
    refreshCntrl.endRefreshing()

}
Run Code Online (Sandbox Code Playgroud)


Luk*_*ker 5

自从Swift 3.0发布以来,声明一个适当的targetAction甚至更加微妙

class MyCustomView : UIView {

    func addTapGestureRecognizer() {

        // the "_" is important
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(MyCustomView.handleTapGesture(_:)))
        tapGestureRecognizer.numberOfTapsRequired = 1
        addGestureRecognizer(tapGestureRecognizer)
    }

    // since Swift 3.0 this "_" in the method implementation is very important to 
    // let the selector understand the targetAction
    func handleTapGesture(_ tapGesture : UITapGestureRecognizer) {

        if tapGesture.state == .ended {
            print("TapGesture detected")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)