我正试图创造一个NSTimer分的Swift,但我遇到了一些麻烦.

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

100 is a function in the same class.


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

找不到接受所提供内容的"init"的重载

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

我试过:

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

但什么都不管用,我在参考资料中也找不到解决办法.

推荐答案

Swift itself不使用 Select 器——Objective-C中使用 Select 器的几种设计模式在Swift中的工作方式不同.(例如,在协议类型或is/as测试上使用可选链接,而不是respondsToSelector:,并尽可能使用闭包,而不是performSelector:,以提高类型/内存安全性.)

但仍有许多重要的基于ObjC的API使用 Select 器,包括计时器和目标/动作模式.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表达式与实际存在且有资格用作 Select 器的类/方法对一起使用(请参阅下面的" Select 器可用性").您还可以根据需要自由地进行函数引用,具体到the Swift 2.2+ rules for function-type naming.

(这实际上是对ObjC的@selector()指令的改进,因为编译器的-Wundeclared-selector判断只验证指定的 Select 器是否存在.传递给#selector的Swift函数引用判断存在性、类中的成员资格和类型签名.)

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

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

一般说明:

Cases where 100 doesn't work, and naming:有时,您没有函数引用来创建 Select 器(例如,在ObjC运行时动态注册的方法).在这种情况下,你可以从一个字符串中构造一个Selector:例如Selector("dynamicMethod:")——尽管你失go 了编译器的有效性判断.这样做时,需要遵循ObjC命名规则,包括每个参数的冒号(:).

Selector availability: Select 器引用的方法必须公开给ObjC运行时.在Swift 4中,expose 于ObjC的每个方法的声明必须以@objc属性开头.(在以前的版本中,在某些情况下可以免费获得该属性,但现在必须显式声明它.)

记住,private个符号也不会expose 在运行时中——您的方法需要至少有internal个可见性.

Key paths:这些与 Select 器有关,但并不完全相同.Swift 3中也有一个特殊的语法:例如chris.valueForKeyPath(#keyPath(Person.friends.firstName)).详情见SE-0062.更重要的是,如果合适的话,请确保使用正确的基于键路径的API,而不是 Select 器.

你可以在Using Swift with Cocoa and Objective-C中阅读更多关于Interacting with Objective-C APIs以下 Select 器的信息.

Note: Swift 2.2之前,Selector符合StringLiteralConvertible,所以您可能会发现旧代码,其中裸字符串被传递给使用 Select 器的API.您需要在Xcode中运行"Convert to Current Swift Syntax",以获得使用#selector的语法

Swift相关问答推荐

如何在SWIFT中解码Instagram Backup中的字符串?

如何分解阿拉伯字母?

并发访问SWIFT中数组的非重叠子范围

如何在SWIFT任务中判断当前任务是否已取消(异步/等待)

将变量设置为 @Published 会 destruct 代码而不会出现任何错误

SwiftUI 拖放:如果没有上下文类型,则无法解析对成员‘first’的引用

在SwiftUI中如何预览嵌套的ScrollView,以避免触发可刷新修饰符

Swift:结果的失败类型不能是协议 - Type 'any ShadowError' cannot conform to Error

Swift 包管理器 Package.resolved 文件末尾的版本是什么意思?

Swift:创建用于调用 C 函数的指针数组

如何在 ZStack 中单独下移卡片?

如何让不同的组件拥有不同的动画?

Swift 4 Decodable - 以枚举为键的字典

Swift 中 NSFetchRequest 的多个 NSPredicates?

为什么我可以转换为 NSManagedObject 但不能转换为我的实体类型?

Type 应该采用什么协议来让泛型函数将任何数字类型作为 Swift 中的参数?

如何以编程方式读取 wkwebview 的控制台日志(log)

如何在 SwiftUI 中为删除和禁用配置 ContextMenu 按钮?

强制打开已在同一行代码中 Select 性访问的变量是否安全?

Swift 中的 recursiveDescription 方法?