本文翻译自:@selector() in Swift?
I'm trying to create an NSTimer
in Swift
but I'm having some trouble. 我正在尝试在Swift
创建NSTimer
,但遇到了一些麻烦。
NSTimer(timeInterval: 1, target: self, selector: test(), userInfo: nil, repeats: true)
test()
is a function in the same class. test()
是同一类中的函数。
I get an error in the editor: 我在编辑器中收到一个错误:
Could not find an overload for 'init' that accepts the supplied arguments 找不到接受提供的参数的'init'的重载
When I change selector: test()
to selector: nil
the error disappears. 当我将selector: test()
更改为selector: nil
,错误消失了。
I've tried: 我试过了:
-
selector: test()
-
selector: test
-
selector: Selector(test())
But nothing works and I can't find a solution in the references. 但是没有任何效果,我在参考文献中找不到解决方案。
#1楼
参考:https://stackoom.com/question/1cjUA/Swift中的-selector
#2楼
Swift itself doesn't use selectors — several design patterns that in Objective-C make use of selectors work differently in Swift. Swift 本身不使用选择器-在Objective-C中,使用选择器的几种设计模式在Swift中的工作方式有所不同。 (For example, use optional chaining on protocol types or is
/ as
tests instead of respondsToSelector:
, and use closures wherever you can instead of performSelector:
for better type/memory safety.) (例如,使用任选的链上的协议类型或is
/ as
测试来代替respondsToSelector:
和使用闭随时随地可以代替performSelector:
为更好的类型/存储器的安全性。)
But there are still a number of important ObjC-based APIs that use selectors, including timers and the target/action pattern. 但是仍然有许多重要的基于ObjC的API使用选择器,包括计时器和目标/操作模式。 Swift provides the Selector
type for working with these. Swift提供了用于处理这些内容的Selector
类型。 (Swift automatically uses this in place of ObjC's SEL
type.) (Swift会自动使用它代替ObjC的SEL
类型。)
In Swift 2.2 (Xcode 7.3) and later (including Swift 3 / Xcode 8 and Swift 4 / Xcode 9): 在Swift 2.2(Xcode 7.3)和更高版本(包括Swift 3 / Xcode 8和Swift 4 / Xcode 9)中:
You can construct a Selector
from a Swift function type using the #selector
expression. 您可以使用#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)
The great thing about this approach? 这种方法的好处是什么? A function reference is checked by the Swift compiler, so you can use the #selector
expression only with class/method pairs that actually exist and are eligible for use as selectors (see "Selector availability" below). Swift编译器会检查函数引用,因此您只能将#selector
表达式与实际存在且有资格用作选择器的类/方法对一起使用(请参见下面的“选择器可用性”)。 You're also free to make your function reference only as specific as you need, as per the Swift 2.2+ rules for function-type naming . 根据Swift 2.2+函数类型命名规则,您还可以随意根据需要指定函数引用。
(This is actually an improvement over ObjC's @selector()
directive, because the compiler's -Wundeclared-selector
check verifies only that the named selector exists. The Swift function reference you pass to #selector
checks existence, membership in a class, and type signature.) (这实际上是对ObjC的@selector()
指令的改进,因为编译器的-Wundeclared-selector
检查仅验证命名选择器的存在。传递给#selector
的Swift函数引用将检查存在性,类的成员身份和类型签名。 )
There are a couple of extra caveats for the function references you pass to the #selector
expression: 对于传递给#selector
表达式的函数引用,还有一些其他警告:
- Multiple functions with the same base name can be differentiated by their parameter labels using the aforementioned syntax for function references (eg
insertSubview(_:at:)
vsinsertSubview(_:aboveSubview:)
). 使用上述用于函数引用的语法 (例如,insertSubview(_:at:)
与insertSubview(_:aboveSubview:)
),可以通过其参数标签来区分具有相同基本名称的多个函数。 But if a function has no parameters, the only way to disambiguate it is to use anas
cast with the function's type signature (egfoo as () -> ()
vsfoo(_:)
). 但是,如果一个函数没有参数,消除歧义的唯一方式是使用as
投用函数的类型签名(例如foo as () -> ()
VSfoo(_:)
)。 - There's a special syntax for property getter/setter pairs in Swift 3.0+. Swift 3.0+中的属性getter / setter对有一种特殊的语法。 For example, given a
var foo: Int
, you can use#selector(getter: MyClass.foo)
or#selector(setter: MyClass.foo)
. 例如,给定var foo: Int
,可以使用#selector(getter: MyClass.foo)
或#selector(setter: MyClass.foo)
。
General notes: 一般注意事项:
Cases where #selector
doesn't work, and naming: Sometimes you don't have a function reference to make a selector with (for example, with methods dynamically registered in the ObjC runtime). #selector
无法使用和命名的情况:有时您没有函数引用来创建选择器(例如,使用在ObjC运行时中动态注册的方法)。 In that case, you can construct a Selector
from a string: eg Selector("dynamicMethod:")
— though you lose the compiler's validity checking. 在那种情况下,您可以从字符串构造一个Selector
:例如Selector("dynamicMethod:")
—尽管您丢失了编译器的有效性检查。 When you do that, you need to follow ObjC naming rules, including colons ( :
) for each parameter. 当你这样做,你需要遵循ObjC的命名规则,包括冒号( :
)每个参数。
Selector availability: The method referenced by the selector must be exposed to the ObjC runtime. 选择器可用性:选择器引用的方法必须公开给ObjC运行时。 In Swift 4, every method exposed to ObjC must have its declaration prefaced with the @objc
attribute. 在Swift 4中,每个公开给ObjC的方法都必须在其声明的前面加上@objc
属性。 (In previous versions you got that attribute for free in some cases, but now you have to explicitly declare it.) (在以前的版本中,某些情况下您可以免费获得该属性,但是现在您必须显式声明它。)
Remember that private
symbols aren't exposed to the runtime, too — your method needs to have at least internal
visibility. 请记住, private
符号也不会暴露给运行时-您的方法至少需要具有internal
可见性。
Key paths: These are related to but not quite the same as selectors. 关键路径:这些路径与选择器相关,但并不完全相同。 There's a special syntax for these in Swift 3, too: eg chris.valueForKeyPath(#keyPath(Person.friends.firstName))
. Swift 3中也有特殊的语法:例如chris.valueForKeyPath(#keyPath(Person.friends.firstName))
。 See SE-0062 for details. 有关详细信息,请参见SE-0062 。 And even more KeyPath
stuff in Swift 4 , so make sure you're using the right KeyPath-based API instead of selectors if appropriate. 还有Swift 4中的更多KeyPath
内容 ,因此请确保您使用的是正确的基于KeyPath的API,而不是适当的选择器。
You can read more about selectors under Interacting with Objective-C APIs in Using Swift with Cocoa and Objective-C . 您可以在“ 将Swift与Cocoa和Objective-C结合使用”中的“ 与Objective-C API交互”下阅读有关选择器的更多信息。
Note: Before Swift 2.2, Selector
conformed to StringLiteralConvertible
, so you might find old code where bare strings are passed to APIs that take selectors. 注意:在Swift 2.2之前, Selector
符合StringLiteralConvertible
,因此您可能会发现旧代码,其中裸露的字符串被传递到采用选择器的API。 You'll want to run "Convert to Current Swift Syntax" in Xcode to get those using #selector
. 您将需要在Xcode中运行“转换为当前的Swift语法”,以使用#selector
获得它们。
#3楼
Here's a quick example on how to use the Selector
class on Swift: 这是一个有关如何在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
}
Note that if the method passed as a string doesn't work, it will fail at runtime, not compile time, and crash your app. 请注意,如果以字符串形式传递的方法不起作用,它将在运行时失败,而不是在编译时失败,并导致应用崩溃。 Be careful 小心
#4楼
Selectors are an internal representation of a method name in Objective-C. 选择器是Objective-C中方法名称的内部表示。 In Objective-C "@selector(methodName)" would convert a source-code method into a data type of SEL. 在Objective-C中,“ @ selector(methodName)”会将源代码方法转换为SEL的数据类型。 Since you can't use the @selector syntax in Swift (rickster is on point there), you have to manually specify the method name as a String object directly, or by passing a String object to the Selector type. 由于您不能在Swift中使用@selector语法(此处就是rickster),因此您必须直接将方法名称直接指定为String对象,或者将String对象传递给Selector类型。 Here is an example: 这是一个例子:
var rightBarButton = UIBarButtonItem(
title: "Logout",
style: UIBarButtonItemStyle.Plain,
target: self,
action:"logout"
)
or 要么
var rightBarButton = UIBarButtonItem(
title: "Logout",
style: UIBarButtonItemStyle.Plain,
target: self,
action:Selector("logout")
)
#5楼
Also, if your (Swift) class does not descend from an Objective-C class, then you must have a colon at the end of the target method name string and you must use the @objc property with your target method eg 另外,如果您的(Swift)类不是Objective-C类的子类,那么您必须在目标方法名称字符串的末尾加一个冒号,并且必须将@objc属性与目标方法一起使用,例如
var rightButton = UIBarButtonItem(title: "Title", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("method"))
@objc func method() {
// Something cool here
}
otherwise you will get a "Unrecognised Selector" error at runtime. 否则,您将在运行时收到“无法识别的选择器”错误。
#6楼
you create the Selector like below. 您可以如下所示创建选择器。
1. 1。
UIBarButtonItem(
title: "Some Title",
style: UIBarButtonItemStyle.Done,
target: self,
action: "flatButtonPressed"
)
2. 2。
flatButton.addTarget(self, action: "flatButtonPressed:", forControlEvents: UIControlEvents.TouchUpInside)
Take note that the @selector syntax is gone and replaced with a simple String naming the method to call. 请注意,@ selector语法已消失,并被命名为要调用的方法的简单String取代。 There's one area where we can all agree the verbosity got in the way. 在一个领域中,我们都可以同意冗长的方式。 Of course, if we declared that there is a target method called flatButtonPressed: we better write one: 当然,如果我们声明有一个名为flatButtonPressed的目标方法:我们最好编写一个:
func flatButtonPressed(sender: AnyObject) {
NSLog("flatButtonPressed")
}
set the timer: 设置计时器:
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
In order to be complete, here's the flatButtonPressed 为了完整,这里是flatButtonPressed
func flatButtonPressed(timer: NSTimer) {
}