Swift中检测类型类一致性、安全遵循AnyObject的实现方案
编者注:评论中提及的此前答案并未解答核心问题:如何判断任意给定Type是否为引用类型,以及如何让该类型安全遵循AnyObject。
直接对传入类型做检测无法生效,因为底层类型可能是可选类型,也可能是协议,这类场景下需要判断传入的实例是类类型还是值类型。
笔者梳理的解决方案与下文给出的修订答案思路相近。
我开发了一款全新的依赖注入框架Factory。
Factory支持作用域实例能力,本质是在服务创建完成后对其进行缓存。其中一类作用域为shared:只要外部存在对共享实例的强引用,该实例就会被缓存并复用;当最后一个强引用释放后,缓存也会释放该实例,下一次解析时将创建新实例。
该能力的实现逻辑非常直观:对创建的对象维持弱引用即可,若弱引用为nil则重新创建对象。
但该实现存在核心问题:弱引用仅能作用于引用类型。
Factory内部通过泛型管理类型信息,支持创建任意类型的实例:类、结构体、字符串等均可支持。
作用域内部使用装箱类型的字典存储实例,若缓存和装箱容器中存在对应实例则直接返回。我最初期望实现如下代码:
private struct WeakBox<T:AnyObject>: AnyBox { weak var boxed: T }
声明T遵循AnyObject是为了支持弱引用,否则会触发编译器报错。随后我期望通过如下代码在shared作用域中装箱并缓存对象:
func cache<T>(id: Int, instance: T) { cache[id] = WeakBox(boxed: instance) }
但上述代码同样会触发编译器报错:泛型结构体WeakBox要求T必须是类类型。
如何实现两类类型的桥接?如下写法无法生效,Swift会抛出警告“从'T'到'AnyObject'的条件转换始终成功”,并直接完成类型转换:
func cache<T>(id: Int, instance: T) { if let instance = instance as? AnyObject { cache[id] = WeakBox(boxed: instance) } }
我也尝试过如下写法,但存在同样问题:无法检测类型是否符合类一致性,也无法通过条件转换判断是否遵循AnyObject,该转换始终会成功:
private struct WeakBox: AnyBox { weak var boxed: AnyObject? } func cache<T>(id: Int, instance: T) { if let instance = instance as? AnyObject { cache[id] = WeakBox(boxed: instance) } }
目前我采用的临时实现如下:
private struct WeakBox: AnyBox { weak var boxed: AnyObject? } func cache<T>(id: Int, instance: T) { cache[id] = WeakBox(boxed: instance as AnyObject) }
该实现虽然可以运行,但其中instance as AnyObject的转换依赖于特殊的Swift与Objective-C桥接行为,稳定性不足。
无法在运行时检测类型的类一致性对开发造成了较大困扰,属于Swift语言的一处明显设计缺陷:既无法完成一致性检测,也无法通过类型转换判定一致性,该场景下需要合规的解决方案。
内容的提问来源于stack exchange,提问作者Michael Long

