ios - #selector() 的目标是否取决于 `addObserver` 的上下文(类与对象)?

标签 ios swift nsnotificationcenter

下面的完整测试用例应该演示:一个选择器,即使它在两个地方被指定为相同的,但执行方式不同:要么在类上执行,要么在对象上执行。 (我知道静态方法和对象方法可以重名,但下面只有一个。)接收者是类还是对象似乎取决于“相同”的位置 selectorNSNotificationCenter 所知,无论是在类上下文中还是在方法上下文中:

  1. 静态方法调用addObserver,或者
  2. 对象方法调用 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.addObserverUIControl.sendActionNSTimer.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/

相关文章:

objective-c - 合并 NSNotifications

ios - 为什么这个标签看起来没有样式?

ios - 在 iOS 8 中使用自动 UITableViewCell 高度,如果单元格的内容嵌入到 UIView 中,它是否可以工作?

ios - SymmetricDS 可以用于 iOS 吗

objective-c - 无法将类型 'NSNotification.Name' 的值转换为预期的参数类型 'NSKeyValueObservingOptions'

objective-c - 解析通知 userInfo 时出现 NSString IntValue 异常

iphone - 向后编程的 iOS 版本?

swift - 函数作为返回类型 Swift 4

swift - 更改 UICollectionViewCell 内项目的大小

ios - 使用 Xcode、Swift 和 GRDB,为什么我必须解开 DatabaseQueue?在我可以使用它的方法之前?