我正试图在 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())
但没有任何作用,我在参考文献中找不到解决方案 .
22 回答
For swift 3
同一类中的函数声明
Swift 4.1
随着轻拍手势的样本
有关详细信息,请参阅Apple的文档:Selector Expression
为了防止其他人遇到与NSTimer相同的问题,其中没有其他答案解决了这个问题,重要的是要提一下,如果你使用的是一个不直接或深层次地从NSObject继承的类(例如,手动创建快速文件),即使指定如下,其他任何答案都不会起作用:
除了让类继承自NSObject之外没有改变任何其他东西我停止了“无法识别的选择器”错误并使我的逻辑按预期工作 .
Swift 4中的选择器:
//刷新控制方法
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
.关于这种方法的好处是什么?函数引用由Swift编译器检查,因此您只能将
#selector
表达式用于实际存在且可用作选择器的类/方法对(请参阅下面的"Selector availability") . 根据the Swift 2.2+ rules for function-type naming,您也可以根据需要自由地制作功能参考 .(这实际上是对ObjC的
@selector()
指令的改进,因为编译器的-Wundeclared-selector
检查仅验证指定的选择器是否存在 . 传递给#selector
的Swift函数引用检查存在,类的成员资格和类型签名 . )对于传递给
#selector
表达式的函数引用,还有一些额外的注意事项:具有相同基本名称的多个函数可以使用上述syntax for function references(例如
insertSubview(_:at:)
vsinsertSubview(_:aboveSubview:)
)通过参数标签区分 . 但是如果函数没有参数,则消除歧义的唯一方法是使用带有函数类型签名的as
强制转换(例如foo as () -> ()
vsfoo(_:)
) .Swift 3.0中的属性getter / setter对有一个特殊的语法 . 例如,给定
var foo: Int
,您可以使用#selector(getter: MyClass.foo)
或#selector(setter: MyClass.foo)
.一般说明:
Cases where #selector doesn't work, and naming: 有时您没有函数引用来创建选择器(例如,使用在ObjC运行时中动态注册的方法) . 在这种情况下,您可以从字符串构造
Selector
:例如Selector("dynamicMethod:")
- 虽然您丢失了编译器的有效性检查 . 当您这样做时,您需要遵循ObjC命名规则,包括每个参数的冒号(:
) .Selector availability: 选择器引用的方法必须公开给ObjC运行时 . 在Swift 4中,暴露给ObjC的每个方法都必须以
@objc
属性开头 . (在以前的版本中,在某些情况下,您可以免费获得该属性,但现在您必须明确声明它 . )请记住,
private
符号也不会暴露给运行时 - 您的方法需要至少具有internal
可见性 .Key paths: 这些与选择器有关但不完全相同 . 在Swift 3中也有一些特殊的语法:例如
chris.valueForKeyPath(#keyPath(Person.friends.firstName))
. 有关详细信息,请参阅SE-0062 . 甚至更多KeyPath stuff in Swift 4,因此,如果合适,请确保使用正确的基于KeyPath的API而不是选择器 .您可以在使用Swift with Cocoa和Objective-C的Interacting with Objective-C APIs下阅读有关选择器的更多信息 .
注意:在Swift 2.2之前,Selector符合StringLiteralConvertible,因此您可能会发现旧代码,其中将裸字符串传递给采用选择器的API . 您将要在Xcode中运行“转换为当前Swift语法”以获取使用#selector的那些语法 .
Swift 4.0
你创建如下所示的选择器 .
1.将事件添加到如下按钮:
功能如下:
记下你的位置可能很有用设置触发操作的控件很重要 .
例如,我发现在设置UIBarButtonItem时,我必须在viewDidLoad中创建按钮,否则我将得到一个无法识别的选择器异常 .
选择器是Objective-C中方法名称的内部表示 . 在Objective-C中,“@ selector(methodName)”将源代码方法转换为SEL的数据类型 . 由于您无法在Swift中使用@selector语法(rickster就在那里),您必须直接手动将方法名称指定为String对象,或者将String对象传递给Selector类型 . 这是一个例子:
要么
使用
performSelector()
时/addtarget()/NStimer.scheduledTimerWithInterval()
方法您的方法(匹配选择器)应标记为对于Swift 2.2,您需要编写'#selector()'而不是字符串和选择器名称,因此拼写错误和崩溃的可能性将不再存在 . 以下是示例
自从Swift 3.0发布以来,声明一个适当的targetAction甚至更加微妙
使用 #selector 将在编译时检查您的代码,以确保您要调用的方法实际存在 . 更好的是,如果该方法不存在,您将收到编译错误:Xcode将拒绝构建您的应用程序,因此无法忘记另一个可能的错误来源 .
如果您想将参数从NSTimer传递给函数,那么这是您的解决方案:
在选择器文本(tester :)中包含冒号,您的参数在userInfo中 .
您的函数应该将NSTimer作为参数 . 然后只需提取userInfo即可获取传递的参数 .
在调用 selector syntax 的方法中更改为简单的字符串命名
之后,键入func test() .
这是一个关于如何在Swift上使用
Selector
类的简单示例:请注意,如果作为字符串传递的方法不起作用,它将在运行时失败,而不是编译时间,并使您的应用程序崩溃 . 小心
此外,如果您的(Swift)类不是来自Objective-C类,那么您必须在目标方法名称字符串的末尾添加冒号,并且必须将@objc属性与目标方法一起使用,例如:
否则您将在运行时收到“无法识别的选择器”错误 .
For Swift 3
//创建计时器的示例代码
对于未来的读者,我发现我遇到了一个问题,并且因为将目标
func
标记为私有而导致 unrecognised selector sent to instance 错误 .func
MUST 是公开可见的,由具有对选择器的引用的对象调用 .我发现许多这些答案都很有用,但不清楚如何用不是按钮的东西来做这件事 . 我正在快速地向UILabel添加一个手势识别器,并且在阅读以上所有内容后,我发现这对我有用:
“选择器”被声明为:
请注意,它是公共的,我没有使用Selector()语法,但也可以这样做 .
你创建如下所示的选择器 .
1 .
2 .
请注意,@ selector语法已消失,并替换为命名要调用的方法的简单String . 有一个领域我们都可以同意这种冗长的方式 . 当然,如果我们声明有一个名为flatButtonPressed的目标方法:我们最好写一个:
设置计时器:
为了完整,这里是flatButtonPressed
Swift 2.2+ and Swift 3 Update
使用新的
#selector
表达式,这消除了使用字符串文字的需要,使得使用不易出错 . 以供参考:变
另见:Swift Evolution Proposal
Note (Swift 4.0):
如果使用
#selector
,则需要将该函数标记为@objc
例:
@objc func something(_ sender: UIButton)