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

自定义AnyHashableSendable:如何实现原生AnyHashable的便捷特性?

问题

升级Xcode 16后,Apple将AnyHashable标记为非Sendable,导致项目中大量使用AnyHashable的场景出现警告(例如ViewModel层返回的UITableViewDiffableDataSource<Int, AnyHashable>)。为了兼容Sendable协议且减少重构工作量,仿照AnyHashable实现了AnyHashableSendable结构体,但存在两处使用不便:

  1. 类型转换繁琐:原生AnyHashable可直接通过foo as? Bar做类型转换,而AnyHashableSendable必须访问base属性,即foo.base as? Bar;
  2. 数组操作繁琐:定义[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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 04:42:16