如何让Binding扩展支持非Sendable类型,实现非空时显示详情Sheet?
问题:适配非Sendable类型的Binding.notNilBinding扩展
问题背景
需要在optionalDetailItem非空时显示详情sheet,且不额外引入布尔@State变量,当前调用代码如下:
.sheet(isPresented: $optionalDetailItem.notNilBinding()) { Text("Details") }
现有Binding扩展要求泛型T遵循Sendable协议,移除该约束后出现编译错误:
Capture of 'self' with non-sendable type 'Binding<Optional
>' in a '@Sendable' closure
原扩展代码:
@available(iOS 13.0, macOS 10.15, *) public extension Binding { /// Careful, never set this to `true`. func notNilBinding<T: Sendable>() -> Binding<Bool> where Value == Optional<T> { Binding<Bool>( get: { self.wrappedValue != nil }, set: { newValue in if !newValue { wrappedValue = nil } else { // In this context we cannot not know what to set `wrappedValue` to. fatalError("This must not be set to `true`.") } } ) } }
解决方案
错误根源是SwiftUI的Binding构造器默认要求闭包为@Sendable,当Binding<Optional<T>>的T不遵循Sendable时,捕获self会触发Sendability检查。以下两种方法可解决:
方法一:显式标记闭包为@unchecked Sendable
直接在set闭包上添加@unchecked Sendable,告知编译器我们确认闭包的Sendability安全(仅将wrappedValue设为nil,无跨线程传递非Sendable实例的风险):
@available(iOS 13.0, macOS 10.15, *) public extension Binding { /// 注意:请勿将此Binding的值设为true func notNilBinding<T>() -> Binding<Bool> where Value == Optional<T> { Binding<Bool>( get: { self.wrappedValue != nil }, set: { @unchecked Sendable [self] newValue in guard !newValue else { fatalError("此Binding不允许被设为true") } wrappedValue = nil } ) } }
方法二:通过辅助协议重构扩展
定义OptionalType协议来约束Optional类型,让扩展逻辑更清晰,同时规避泛型约束的Sendable限制:
@available(iOS 13.0, macOS 10.15, *) public extension Binding where Value: OptionalType { /// 注意:请勿将此Binding的值设为true func notNilBinding() -> Binding<Bool> { Binding<Bool>( get: { self.wrappedValue != nil }, set: { @unchecked Sendable [self] newValue in guard !newValue else { fatalError("此Binding不允许被设为true") } wrappedValue = nil } ) } } // 辅助协议,用于匹配所有Optional类型 public protocol OptionalType { associatedtype Wrapped var wrappedValue: Wrapped? { get set } } extension Optional: OptionalType { public var wrappedValue: Wrapped? { get { self } set { self = newValue } } }
注意事项
- 使用
@unchecked Sendable时,需确保闭包内不会将非Sendable的T实例传递到其他线程,避免潜在的数据竞争风险。 - 两种方法都保留了原逻辑:仅允许将Binding设为false(清空optional值),设为true会触发fatalError。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

