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

Swift中能否在工厂类中使用泛型替代AnyObject?

问题

我尝试在用于创建泛型类实例的工厂类静态变量中存储该泛型类的实例,但无法为工厂类的instances变量设置正确的类型。目前我使用了AnyObject并通过强制类型转换来实现功能,但似乎应有更好的实现方式,我希望不使用AnyObject且不进行强制转换。请问这是否可行?若可行,我需要做出哪些改动?

原代码:

class StorageFactory {
  /// Instances of the storage, note the AnyObject here
  private static var instances: [String: AnyObject] = [:]

  private init() {}

  /// Create or return an existing shared storage instance
  static func create<Key: Hashable, Value>(name: String) -> Storage<Key, Value> {
    if let instance = instances[name] {
      // In order to make this work I have to force cast, but I don't want to
      // swiftlint:disable:next force_cast
      return instance as! Storage<Key, Value>
    }

    instances[name] = Storage<Key, Value>()

    return create(name: name)
  }
}

/// Class using generics
class Storage<Key, Value> where Key: Hashable {
  /// Dictionary for storing data
  private var data = [Key: Value]()

  /// Get an item from the storage by its key
  func get(forKey key: Key) -> Value? {
    return data[key]
  }

  /// Set an item from the storage by its key
  func set(_ value: Value, forKey key: Key) {
    data[key] = value
  }

  /// Get an item from the storage by its key, and when it does not exist provide a closure for setting it
  ///
  /// The closure is only called if the value does not already exist inside of the storage
  func getOrSet(forKey key: Key, setter: () -> Value?) -> Value? {
    if let value = get(forKey: key) {
      return value
    }

    if let newValue = setter() {
      set(newValue, forKey: key)
      return newValue
    }

    return nil
  }
}

// Used something like...

let date = Date()
let value = StorageFactory.create(name: "longDate").getOrSet(forKey: date) {
  date.formatted(date: .long, time: .omitted)
} ?? ""
解决方案

可行。核心思路是用类型擦除包装器统一存储不同泛型参数的Storage实例,同时保留类型安全的访问方式,避免强制转换和AnyObject。

具体改动步骤:

  • 定义一个类型擦除的包装类,作为所有Storage实例的统一存储类型
  • 修改StorageFactory的instances存储类型为该包装类
  • 在工厂方法中通过包装类的类型安全转换方法获取对应泛型的Storage实例

修改后的完整代码:

// 类型擦除包装器,统一所有Storage实例的存储类型
private class AnyStorage {
    private let wrapped: Any
    init<T: Hashable, U>(_ storage: Storage<T, U>) {
        self.wrapped = storage
    }
    
    // 安全转换为指定泛型参数的Storage实例
    func castTo<T: Hashable, U>() -> Storage<T, U>? {
        return wrapped as? Storage<T, U>
    }
}

class StorageFactory {
    // 存储类型改为AnyStorage,不再依赖AnyObject
    private static var instances: [String: AnyStorage] = [:]

    private init() {}

    static func create<Key: Hashable, Value>(name: String) -> Storage<Key, Value> {
        if let existingStorage = instances[name]?.castTo() {
            return existingStorage
        }
        
        let newStorage = Storage<Key, Value>()
        instances[name] = AnyStorage(newStorage)
        return newStorage
    }
}

/// 原泛型Storage类保持不变
class Storage<Key, Value> where Key: Hashable {
    private var data = [Key: Value]()

    func get(forKey key: Key) -> Value? {
        return data[key]
    }

    func set(_ value: Value, forKey key: Key) {
        data[key] = value
    }

    func getOrSet(forKey key: Key, setter: () -> Value?) -> Value? {
        if let value = get(forKey: key) {
            return value
        }

        if let newValue = setter() {
            set(newValue, forKey: key)
            return newValue
        }

        return nil
    }
}

// 使用方式完全不变
let date = Date()
let value = StorageFactory.create(name: "longDate").getOrSet(forKey: date) {
    date.formatted(date: .long, time: .omitted)
} ?? ""

改动说明:

  • AnyStorage作为中间层,包装了任意泛型参数的Storage实例,同时提供castTo()方法做类型安全转换,彻底避免强制解包的崩溃风险
  • StorageFactory的instances现在存储的是明确的AnyStorage类型,不再依赖模糊的AnyObject,代码可读性和安全性提升
  • 若同一name对应不同泛型参数的Storage请求,工厂会自动创建新实例,不会出现类型不匹配的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 05:23:23