Swift 5:如何在对象上执行选择器?

jhkqcmku  于 2023-03-28  发布在  Swift
关注(0)|答案(1)|浏览(123)

作为练习,我正在实现NSNotificationCenter。我最初用于保存观察者的Map的数据结构是:

protocol CustomObserver : Hashable {
  func receiveNotification(userInfo: [AnyHashable : Any]?)
}

//....class declaration and init for CustomNotificationCenter

private var notificationsMap: [String: Set<CustomObservers>] = [:]

其中CustomObservers将有一个receiveNotification(...)调用。然而,我发现我不能在Swift中拥有一个协议集:

// This syntax did not work given the above code blocks.
Set<CustomObserver>

在大量的堆栈溢出之后,我找不到一个优雅的解决方案。所有的解决方案似乎都涉及:
1.制定观察员协议。
1.创建一个符合观察者协议的类。
1.使所有观察者都成为上述类的子类。
如果任何人有一个不涉及创建新类的Set的优雅解决方案,我洗耳恭听。在此之前...
我决定尝试使用这个数据结构:

private var notificationsMap: [String: [AnyHashable: Selector]] = [:]

这样我就不用依赖协议来定义receiveNotification行为,而是用一个选择器来调用订阅通知的每个对象。
在NotificationCenter的post函数中,我有这样的代码:
1.查找与通知名称关联的字典。
1.遍历字典,在每个对象上调用选择器。

func post(name: String, object: Any?, userInfo: [AnyHashable : Any]?) {
    if let obsArray = notificationsMap[name] {
      for (object, selector) in obsArray {
        object.performSelector(onMainThread: selector, withObject: nil, waitUntilDone: true)
      }
    }
  }

但是,在performSelector行中,我得到了以下错误:

Value of type 'AnyHashable' has no member 'performSelector'

那么我该怎么做呢?我该怎么执行Selector?或者...我该怎么让Set工作呢?

q3aa0525

q3aa05251#

我不知道你是否会称之为“优雅的解决方案”,但我会使用类型擦除,这与AnyCollectionAnyPublisher中使用的原理相同:

class AnyCustomObserver: CustomObserver {
    private let id = UUID() // needed for Hashable conformance
    private let handleNotification: ([AnyHashable: Any]?) -> Void
    
    init(_ observer: any CustomObserver) {
        handleNotification = observer.receiveNotification(userInfo:)
    }
    
    func receiveNotification(userInfo: [AnyHashable : Any]?) {
        handleNotification(userInfo)
    }
}

extension AnyCustomObserver: Equatable {
    static func == (lhs: AnyCustomObserver, rhs: AnyCustomObserver) -> Bool {
        lhs.id == rhs.id
    }
}

extension AnyCustomObserver: Hashable {
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
}

现在可以实现notificationsMap属性了

private var notificationsMap: [String: Set<AnyCustomObserver>] = [:]
备注

为了简单起见,我添加了一个UUID来满足EquatableHashable的要求,但在CustomObserver中使用标识符或名称可能更好。

相关问题