使用私有全局变量作为extension的‘属性’是否属于代码坏味道?
关于Swift扩展中模拟存储属性的替代方案讨论
我完全懂你这种纠结——Swift的extension确实不允许直接添加存储属性,用objc_getAssociatedObject/objc_setAssociatedObject虽然能实现类似效果,但总感觉是在用Objective-C的老思路硬套Swift,完全不符合Swift的设计哲学,说它“unswifty”真的一点没错。
你提到的用私有全局变量来模拟extension的“属性”,其实是一种很巧妙的“曲线救国”思路,虽然讨论不多,但确实有它的适用场景。举个简单的实现例子:
// 私有全局字典,仅当前文件可见 private var viewControllerCustomDataMap: [UIViewController: String] = [:] extension UIViewController { // 模拟的存储属性 var customIdentifier: String { get { return viewControllerCustomDataMap[self] ?? "default_id" } set { viewControllerCustomDataMap[self] = newValue } } }
这种方案的优缺点分析
优势:
- 完全基于Swift原生语法实现,没有Objective-C Runtime的依赖,更贴合Swift的设计风格
- 私有全局变量仅在当前文件可见,不会污染全局命名空间,和扩展的关联关系非常紧密
- 逻辑简单直观,比关联对象的代码更容易理解和维护,新手也能快速上手
潜在问题:
- 内存风险:如果全局字典对对象持有强引用,很容易造成循环引用,导致对象无法正常释放。解决这个问题可以给字典的key套一层弱引用包装类,比如自定义
Weak<T>类型 - 类型限制:这种方式依赖于对象的
Hashable协议实现,对于未遵循该协议的类型无法直接使用 - 全局状态管理:本质还是全局变量,当多个扩展或业务逻辑共用这类变量时,可能会增加状态维护的复杂度
- 内存风险:如果全局字典对对象持有强引用,很容易造成循环引用,导致对象无法正常释放。解决这个问题可以给字典的key套一层弱引用包装类,比如自定义
和关联对象方案对比,它避开了Runtime的“黑魔法”,但也失去了关联对象自动绑定对象生命周期的优势——关联对象会在宿主对象释放时自动清理,而全局字典需要我们手动处理引用关系,否则容易出现内存泄漏。
如果你的业务场景中对象数量不多,且能妥善处理内存引用问题,这种私有全局变量的方法确实是一个更“Swifty”的替代方案。当然,如果需要更严谨的生命周期管理,或者涉及非Hashable类型,可能还是得回到关联对象,或者考虑重构代码将属性放到原始类中(如果权限允许的话)。
内容的提问来源于stack exchange,提问作者Tysac
相关产品推荐
相关产品推荐

