You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 5如何对对象执行Selector 或如何实现存储协议类型的Set

方案1:使用类型擦除实现Set存储协议实例(更推荐,无基类要求)

你之前的Set<CustomObserver>写法不可行的原因是:CustomObserver继承了Hashable,而Hashable带有Self约束,这类带Self约束的协议不能直接作为泛型参数的类型使用,只能作为泛型约束。你不需要给所有观察者新增统一基类,只需要实现一个通用的类型擦除包装器即可:

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

struct AnyCustomObserver: CustomObserver {
    private let _receive: ([AnyHashable : Any]?) -> Void
    private let _hash: (inout Hasher) -> Void
    private let _equals: (Any) -> Bool
    
    init<T: CustomObserver>(_ observer: T) {
        _receive = observer.receiveNotification
        _hash = { hasher in
            observer.hash(into: &hasher)
        }
        _equals = { other in
            guard let other = other as? T else { return false }
            return observer == other
        }
    }
    
    func receiveNotification(userInfo: [AnyHashable : Any]?) {
        _receive(userInfo)
    }
    
    func hash(into hasher: inout Hasher) {
        _hash(&hasher)
    }
    
    static func == (lhs: AnyCustomObserver, rhs: AnyCustomObserver) -> Bool {
        lhs._equals(rhs)
    }
}

修改后的存储结构如下:

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

后续添加观察者时,把遵守CustomObserver协议的实例包进AnyCustomObserver再存入Set即可。观察者本身不需要继承任何基类,只要遵守CustomObserver协议就能正常使用。


方案2:修复当前Selector方案的报错

报错原因是AnyHashable是一个包装结构体,本身没有performSelector方法,你需要先取出它包装的原始值,转成AnyObject(类类型)再调用方法,同时建议加响应判断避免崩溃:

func post(name: String, object: Any?, userInfo: [AnyHashable : Any]?) {
    if let obsDict = notificationsMap[name] {
        for (observerWrapper, selector) in obsDict {
            // 取出AnyHashable包装的原始对象,转成类实例
            guard let observer = observerWrapper.base as? AnyObject,
                  observer.responds(to: selector) else {
                continue
            }
            // 如需传递userInfo可以把withObject参数换成userInfo
            observer.performSelector(onMainThread: selector, withObject: userInfo, waitUntilDone: true)
        }
    }
}

注意这个方案依赖OC runtime,只能用于继承自NSObject的类实例,没有编译时类型校验,容易出现未识别选择器的崩溃,不如方案1安全。


内容的提问来源于stack exchange,提问作者Mark S

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 22:24:00