【问题标题】:Swift: setting an optional property of a protocolSwift:设置协议的可选属性
【发布时间】:2023-04-06 07:29:02
【问题描述】:

如何设置协议的可选属性?例如 UITextInputTraits 有许多可选的读/写属性。当我尝试以下操作时,我得到一个编译错误(不能分配给'textInputTraits'中的'keyboardType'):

func initializeTextInputTraits(textInputTraits: UITextInputTraits) {
  textInputTraits.keyboardType = .Default
}

通常在访问协议的可选属性时会添加一个问号,但这在分配值时不起作用(错误:无法分配给此表达式的结果):

textInputTraits.keyboardType? = .Default

协议如下:

protocol UITextInputTraits : NSObjectProtocol {
  optional var keyboardType: UIKeyboardType { get set }
}

【问题讨论】:

    标签: swift


    【解决方案1】:

    在 Swift 中这是不可能的(还没有?)。引用自ADF thread:

    可选的属性要求和返回值的可选方法要求,在被访问或调用时将总是返回适当类型的可选值,以反映可选要求可能尚未实施。

    因此轻松获得可选值也就不足为奇了。但是,设置属性需要保证实现。

    【讨论】:

    • 从 Swift 2.2 开始仍然不可能
    • 从 Swift 4 开始仍然不可能
    • 从 Swift 5.3.2 开始仍然不可能
    【解决方案2】:

    我会考虑制作一个扩展,返回您的 keyboardType 的默认值,并且让 setter 什么都不做。

    extension UITextInputTraits {
       var keyboardType: UIKeyboardType { 
          get { return .default }
          set { /* do nothing */ } 
       }
    }
    

    这样您就可以从您的属性声明中删除optional。

    protocol UITextInputTraits : NSObjectProtocol {
      var keyboardType: UIKeyboardType { get set }
    }
    

    或者,如果您愿意,可以将其设为可选返回类型 var keyboardType: UIKeyboardType?,并在您的扩展程序中返回 nil 而不是 .Default。

    这样,您可以测试myObject.keyboardType == nil 以查看您的属性是否已实现。

    【讨论】:

      【解决方案3】:

      一种解决方法是创建另一个协议,该协议符合第一个协议,但将该可选属性声明为强制。然后尝试将对象转换为新协议,如果转换成功,我们可以直接访问该属性。

      protocol UITextInputTraitsWithKeyboardType : UITextInputTraits {
        // redeclare the keyboardType property of the parent protocol, but this time as mandatory
        var keyboardType: UIKeyboardType { get set }
      }
      
      func initializeTextInputTraits(textInputTraits: UITextInputTraits) {
        // We try to cast ('as?') to the UITextInputTraitsWithKeyboardType protocol
        if let traitsWithKBType = textInputTraits as? UITextInputTraitsWithKeyboardType {
          // if the cast succeeded, that means that the keyboardType property is implemented
          // so we can access it on the casted variable
          traitsWithKBType.keyboardType = .Default
        } 
        // (and if the cast failed, this means that the property is not implemented)
      }
      

      如果你想要这个想法的另一个例子,我还演示了at the bottom on this SO answer。

      【讨论】:

      • 我用 Swift 2.2 试过这个,但是 as?不采取 - 我似乎记得协议实际上是通过他们的名字来评估的。如果核心对象没有实现“UITextInputTraitsWithKeyboardType”,那么它就不会通过。想知道你是如何做到这一点的......
      【解决方案4】:

      以下是我想出的 Swift 2.2 兼容性:

      @objc protocol ComponentViewProtocol: class {
          optional var componentModel: ComponentModelProtocol? { get set }
          optional var componentModels: [String:ComponentModelProtocol]? { get set }
      }
      
      class ComponentView: NSObject, ComponentViewProtocol {
          var componentModel: ComponentModelProtocol?
          ...
          ..
          .
      }
      
      class SomeCodeThatUsesThisStuff {
      
          func aMethod() {
              if var componentModel = componentView.componentModel {
                  componentModel = .... (this succeeds - you can make assignment)
              } else if var componentModels = componentView.componentModels {
                  componentModels = .... (this failed - no assignment possible)
              }
          }
      }
      

      本质上,您需要对可选属性执行“if/let 绑定”以确保它实际被实现?如果它为 nil,则封装对象没有实现它,否则你现在可以对其进行赋值,编译器将不再抱怨。

      【讨论】:

      • 这只是更改了您创建的局部变量 - 它不会更改对象值。
      猜你喜欢
      • 2018-12-31
      • 2016-10-25
      • 1970-01-01
      • 2018-07-15
      • 1970-01-01
      • 2020-12-07
      • 2021-10-02
      • 2023-04-06
      • 1970-01-01
      相关资源
      最近更新 更多