自定义AnyHashableSendable:如何实现原生AnyHashable的便捷特性?
问题
升级Xcode 16后,Apple将AnyHashable标记为非Sendable,导致项目中大量使用AnyHashable的场景出现警告(例如ViewModel层返回的UITableViewDiffableDataSource<Int, AnyHashable>)。为了兼容Sendable协议且减少重构工作量,仿照AnyHashable实现了AnyHashableSendable结构体,但存在两处使用不便:
- 类型转换繁琐:原生
AnyHashable可直接通过foo as? Bar做类型转换,而AnyHashableSendable必须访问base属性,即foo.base as? Bar; - 数组操作繁琐:定义
[AnyHashable]类型数组时可直接追加任意Hashable类型元素,但[AnyHashableSendable]必须手动调用.init()包装元素后才能追加。
现有AnyHashableSendable实现代码:
public struct AnyHashableSendable: Hashable, Sendable { public let base: any Hashable & Sendable public init<H>(_ base: H) where H : Hashable { self.base = base } public static func == (lhs: Self, rhs: Self) -> Bool { AnyHashable(lhs.base) == AnyHashable(rhs.base) } public func hash(into hasher: inout Hasher) { base.hash(into: &hasher) } }
解决方案
1. 实现原生风格的类型转换
原生AnyHashable支持直接as?类型转换,依赖的是Objective-C桥接机制的自动解包逻辑。对于纯Swift结构体AnyHashableSendable,可以通过以下两种方式模拟原生体验:
方式一:无私有API依赖(推荐)
通过自定义方法和模式匹配支持,接近原生as?的使用体验:
extension AnyHashableSendable { // 支持直接调用 `foo.as(Bar.self)` 替代 `foo.base as? Bar` public func `as`<T>(_ type: T.Type) -> T? where T: Hashable & Sendable { base as? T } // 支持switch语句中的模式匹配(如 `case let x as Bar`) public static func ~= <T>(pattern: T.Type, value: Self) -> Bool where T: Hashable & Sendable { value.base is T } }
方式二:完全模拟原生as?语法(依赖私有API,需谨慎)
通过实现_ObjectiveCBridgeable私有协议,让AnyHashableSendable支持直接foo as? Bar的语法(注意:私有API可能在未来Swift版本中失效):
extension AnyHashableSendable: _ObjectiveCBridgeable { public func _bridgeToObjectiveC() -> AnyObject { base as AnyObject } public static func _forceBridgeFromObjectiveC(_ source: AnyObject, result: inout Self?) { guard let base = source as? any Hashable & Sendable else { return } result = Self(base) } public static func _conditionallyBridgeFromObjectiveC(_ source: AnyObject, result: inout Self?) -> Bool { guard let base = source as? any Hashable & Sendable else { return false } result = Self(base) return true } public static func _unconditionallyBridgeFromObjectiveC(_ source: AnyObject?) -> Self { guard let base = source as? any Hashable & Sendable else { fatalError("Cannot bridge to AnyHashableSendable") } return Self(base) } }
2. 实现数组自动包装元素
无需自定义集合类型,通过扩展Array为AnyHashableSendable元素类型添加重载方法,即可实现自动包装:
extension Array where Element == AnyHashableSendable { // 支持直接追加单个Hashable & Sendable类型元素 mutating func append<H>(_ element: H) where H: Hashable & Sendable { append(AnyHashableSendable(element)) } // 支持一次性追加多个元素 mutating func append<H>(contentsOf elements: [H]) where H: Hashable & Sendable { append(contentsOf: elements.map(AnyHashableSendable.init)) } // 支持直接从[H]初始化数组 init<H>(_ elements: [H]) where H: Hashable & Sendable { self = elements.map(AnyHashableSendable.init) } }
使用示例:
// 直接初始化数组 var array: [AnyHashableSendable] = [FooBar(prop1: "test", prop2: "test")] // 直接追加单个元素 array.append(FooBar(prop1: "new", prop2: "item")) // 批量追加元素 array.append(contentsOf: [FooBar(prop1: "batch", prop2: "add")])
内容的提问来源于stack exchange,提问作者Simon McLoughlin
相关产品推荐
相关产品推荐

