检查后在 SwiftUI 中将可选绑定设置为 nil 时出现异常

Posted

技术标签:

【中文标题】检查后在 SwiftUI 中将可选绑定设置为 nil 时出现异常【英文标题】:Exception when setting an optional binding to nil in SwiftUI after checking 【发布时间】:2020-09-18 14:53:52 【问题描述】:

我有一个带有State 变量的视图,它是一个Optional。我通过首先检查可选变量是否为 nil 来渲染视图,如果不是,则强制展开它并使用 Binding 将其传递到子视图中。

但是,如果我在值和 nil 之间切换可选变量,应用程序会崩溃,并且我会在函数 BindingOperations.ForceUnwrapping.get(base:) 中得到 EXC_BAD_INSTRUCTION。如何仅显示“Nil”Text 视图来获得视图的预期功能?

struct ContentView: View 
    @State var optional: Int?
    
    var body: some View 
        VStack 
            if optional == nil 
                Text("Nil")
             else 
                TestView(optional: Binding($optional)!)
            
            
            Button(action: 
                if optional == nil 
                    optional = 0
                 else 
                    optional = nil
                
            ) 
                Text("Toggle")
            
        
    


struct TestView: View 
    @Binding var optional: Int
    
    var body: some View 
        VStack 
            Text(optional.description)
            
            Button(action: 
                optional += 1
            ) 
                Text("Increment")
            
        
    

【问题讨论】:

【参考方案1】:

这是解决此问题的一种可能方法。使用 Xcode 12 / ios 14 测试。

The-Variant! - 永远不要使用可选的状态/绑定和强制解包 :)

Variant1:使用绑定包装器(无其他更改)

CRTestView(optional: Binding(
        get:  self.optional ?? -1 , set: self.optional = $0
    ))

Variant2:按原样传输绑定

struct ContentView: View 
    @State var optional: Int?

    var body: some View 
        VStack 
            if optional == nil 
                Text("Nil")
             else 
                CRTestView(optional: $optional)
            

            Button(action: 
                if optional == nil 
                    optional = 0
                 else 
                    optional = nil
                
            ) 
                Text("Toggle")
            
        
    


struct CRTestView: View 
    @Binding var optional: Int?

    var body: some View 
        VStack 
            Text(optional?.description ?? "-1")

            Button(action: 
                optional? += 1
            ) 
                Text("Increment")
            
        
    

【讨论】:

这在此示例中确实有效,但是,在较大的应用程序中,这意味着必须始终检查不理想的可选选项。进行 nil 检查的目的是避免在子视图中出现可选项。 谢谢,这很有帮助,但我不会将其标记为答案,因为在您完成 nil 检查后提供默认值似乎是多余的。【参考方案2】:

我刚刚找到了一个不涉及手动创建绑定和/或硬编码默认值的解决方案,因此您可以执行以下操作:

if let unwrappedBinding = $optional.unwrappedValue 
  TestView(optional: unwrappedBinding)
 else 
  Text("Nil")

这里是扩展:

protocol OptionalType: ExpressibleByNilLiteral 
  associatedtype Wrapped
  var optional: Wrapped?  get set 

extension Optional: OptionalType 
  var optional: Wrapped? 
    get  return self 
    mutating set  self = newValue 
  


extension Binding where Value: OptionalType 
  /// Converts a Binding with an optional values (`Binding<T?>`) to an optional `Binding<T>?` with an unwrapped value.
  var unwrappedValue: Binding<Value.Wrapped>? 
    guard let unwrappedValue = wrappedValue.optional else 
      return nil
    
    
    return .init(get:  unwrappedValue , set:  wrappedValue.optional = $0 )
  


这个更容易指定默认值的扩展 ($optional.defaulting(to: 0)) 也很方便:

  /// Converts a Binding with an optional values (`Binding<T?>`) to a non-optional (`Binding<T>`) with a default value.
  func defaulting(to defaultValue: Value.Wrapped) -> Binding<Value.Wrapped> 
    .init(get:  self.wrappedValue.optional ?? defaultValue , set:  self.wrappedValue.optional = $0 )
  

【讨论】:

以上是关于检查后在 SwiftUI 中将可选绑定设置为 nil 时出现异常的主要内容,如果未能解决你的问题,请参考以下文章

将 SwiftUI 警报或操作表绑定到值类型模型属性的可选性(当属性为 nil 时显示视图)的好方法是啥?

SwiftUI - 我们如何重新绑定绑定的可选参数?

参数 SwiftUI 中的可选绑定

在 Swift 中将可选绑定转换为错误处理的过程是啥?

SwiftUI 中的可选绑定

可选字符串数组作为绑定参数 SwiftUI [重复]