下面的完整测试用例应该演示:一个选择器,即使它在两个地方被指定为相同的,但执行方式不同:要么在类上执行,要么在对象上执行。 (我知道静态方法和对象方法可以重名,但下面只有一个。)接收者是类还是对象似乎取决于“相同”的位置 selector为 NSNotificationCenter
所知,无论是在类上下文中还是在方法上下文中:
- 静态方法调用
addObserver
,或者 - 对象方法调用
addObserver
而调用在其他方面是相同的。 如果相同的调用发生在静态方法中,那么当稍后处理通知时,系统会尝试调用类上的选择器,而不是对象上的选择器。类(class)没有。使用新的(在 2.2 中)语法可以很好地编译代码。这个结果在意料之中吗?
import XCTest
import class Foundation.NSNotificationCenter // for emphasis
class SelectorTests: XCTestCase {
static let NotificationName = "OneTwoThreeNotification"
override func setUp() {
super.setUp()
}
override func tearDown() {
NSNotificationCenter.defaultCenter().removeObserver(self)
super.tearDown()
}
func addObserverForTestNormal() { // <- HERE
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(SelectorTests.myMethod(_:)), // <- HERE
name: SelectorTests.NotificationName,
object: nil)
}
func testNormal() {
self.addObserverForTestNormal()
NSNotificationCenter.defaultCenter().postNotificationName(
SelectorTests.NotificationName,
object: self)
}
static func addObserverForTestStatic() { // <- HERE
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(SelectorTests.myMethod(_:)), // <- HERE
name: SelectorTests.NotificationName,
object: nil)
}
func testStatic() {
SelectorTests.addObserverForTestStatic()
NSNotificationCenter.defaultCenter().postNotificationName(
SelectorTests.NotificationName,
object: self)
}
func myMethod(x : Int) {
XCTAssert(true)
}
}
一个测试成功,另一个失败。堆栈跟踪和消息的要点是
"+[KuckuckTests.SelectorTests myMethod:]: unrecognized selector sent to class
这种 split ,即从 addObserver
上下文“推断”的类或对象,对于 Objective-C 的老手来说是如此明显,以至于不值得用 #selector
提及>?在这种情况下,您能指出一些文档吗?
编辑:刚刚注意到静态函数调用中的self
addObserver
可能指的是类,而不是某个对象。这使得效果有些合理,并建议程序员应该知道重载名称代表什么......
最佳答案
#selector
表达式与该选择器的使用位置没有任何关系。选择器命名一条消息,但不提及该消息的接收者。您可以使用 #selector
表达式为一个对象的方法创建 Selector
值,然后将该 Selector
值传递给 API(例如NSNotificationCenter.addObserver
或 UIControl.sendAction
或 NSTimer.init
),这将导致使用该选择器向某个完全不同的对象发送消息。
这种松散绑定(bind)是 Cocoa 用于传递这些消息的 Objective-C 运行时的动态特性的有意部分(无论您的选择器引用的函数是在 ObjC 还是 Swift 中构建的)。 #selector
表达式,以及它所依赖的 Swift 函数引用语法,为您提供了一种“分类”强类型选择器的方法,但仅在一端——它们让您验证Selector
您正在构造的值是指特定方法。 (但是一旦你有了一个 Selector
值,它的使用方式就不在 Swift 的控制范围内了。)
您的错误信息(强调):
unrecognized selector sent to class
...表示失败是因为消息被发送到 SelectorTests
类对象(又名元类对象)。也就是说,通过在 static
方法中安排发送给 self
的通知,您要求调用 class func myMethod
,不要 func myMethod
。
self
关键字始终指代负责执行代码的实例:在实例方法中,self
指代当前实例。在类方法内部,self
指的是类对象的(唯一实例)。
关于ios - #selector() 的目标是否取决于 `addObserver` 的上下文(类与对象)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36989475/