如何在Swift严格并发模型中使用关联对象?
在Swift严格并发模型中使用关联对象的替代方案
针对你遇到的全局可变key不符合Swift严格并发要求的问题,以下是几种无需绑定全局actor的解决办法:
方案1:使用静态不可变指针作为关联key
通过创建唯一的不可变指针作为关联对象的key,完全避免共享可变状态。可以利用空类的指针来保证唯一性:
// 定义一个私有空类,用于生成唯一指针 private class AssociationKey {} extension MyProtocol { // 静态不可变的关联key,基于空类的指针生成 private static let myAssociationKey = UnsafeRawPointer( Unmanaged.passUnretained(AssociationKey.self).toOpaque() ) var myAddedString: String? { get { objc_getAssociatedObject(self, Self.myAssociationKey) as? String } set { objc_setAssociatedObject(self, Self.myAssociationKey, newValue, .OBJC_ASSOCIATION_COPY) } } }
原理
- 每个空类的类型指针是全局唯一的,不会和其他关联对象的key冲突;
myAssociationKey是静态let常量,属于不可变状态,完全符合Swift严格并发模型的要求,编译器不会抛出并发安全警告。
方案2:用@unchecked Sendable标记全局key变量
如果你想保留原来的全局变量方式,可以通过@unchecked Sendable告诉编译器该变量的实际访问是安全的——因为你从未修改过myKey的内容,只是利用它的内存地址作为key,不存在数据竞争:
// 用@unchecked Sendable标记,说明我们保证该变量的并发访问安全 @unchecked private var myKey: Void? extension MyProtocol { var myAddedString: String? { get { objc_getAssociatedObject(self, &myKey) as? String } set { objc_setAssociatedObject(self, &myKey, newValue, .OBJC_ASSOCIATION_COPY) } } }
注意事项
- 必须确保永远不会修改
myKey的取值,只使用它的内存地址; @unchecked Sendable需要你自行保证安全,若后续不小心修改了变量,会引入数据竞争风险。
方案3:利用静态内部结构体的常量key
可以在属性的get/set方法内部定义静态结构体,用其静态属性作为key,既保证唯一性,又避免全局可变状态:
extension MyProtocol { var myAddedString: String? { get { // 静态结构体的静态属性只会初始化一次,保证key唯一 struct Key { static let value = UnsafeRawPointer( Unmanaged.passUnretained(Key.self).toOpaque() ) } return objc_getAssociatedObject(self, Key.value) as? String } set { struct Key { static let value = UnsafeRawPointer( Unmanaged.passUnretained(Key.self).toOpaque() ) } objc_setAssociatedObject(self, Key.value, newValue, .OBJC_ASSOCIATION_COPY) } } }
原理
- 每个
Key结构体的类型指针是唯一的,且静态属性value只会初始化一次; - 结构体定义在方法内部,不会产生全局可变状态,符合并发安全要求。
内容的提问来源于stack exchange,提问作者Plastech
相关产品推荐
相关产品推荐

