Swift:比较隐式解包的Optionals会导致“在展开Optional值时意外发现nil”

时间:2015-09-21 18:05:09

标签: ios swift swift2 ios9

如果得到这个类,并希望根据值

比较它的实例
class LocationOption: NSObject {
  var name:String!
  var radius:Int!
  var value:String!

  override func isEqual(object: AnyObject?) -> Bool {
    if let otherOption = object as? LocationOption {
        return (self.name == otherOption.name) &&
            (self.radius == otherOption.radius) &&
            (self.value == otherOption.value)
    }
    return false
  }
}

执行此操作时:

var a = LocationOption()
var b = LocationOption()

a.name = "test"
b.name = "test"

if a == b {  // Crashes here!!
  print("a and b are the same");
}

这会在“展开可选值时意外发现nil”崩溃?您可以将所有这些复制到Playground中进行重现。

这似乎是由于Implicitly Unwrapped Optionals。如果我将所有字段声明为Optionals,它将按预期工作。

但就我而言,我希望将这些属性设为Implicitly Unwrapped Optionals。我该如何写isEqual?

===

更新:@matt是对的,因为我不想改为“常规”选项,我最终得到了这个:

class LocationOption: Equatable {
    var name:String!
    var requireGps:Bool!
    var radius:Int!
    var value:String!
}

func ==(lhs: LocationOption, rhs: LocationOption) -> Bool {
    let ln:String? = lhs.name as String?, rn = rhs.name  as String?
    let lr = lhs.radius as Int?, rr = rhs.radius as Int?
    return ln == rn && lr == rr
}

1 个答案:

答案 0 :(得分:4)

您无法解开nil。这就是法律。你不能仅仅通过使用隐式解包的Optionals来解决这个问题。您隐式解包的选项仍然是Optionals。因此,当您的测试引用self.radiusself.value并且您没有设置它们时,它们就是零。所以你试图解开nil然后崩溃。

事实上,如果隐式展开的Optionals你的使用会让事情变得更糟。当您将它们声明为正常的Optionals时,它们不会被隐式解包,因此它们根本不会被打开 - 将正常的Optionals与==运算符进行比较是安全的,因为编译器会为您插入nil测试。因此,法律从未被违反。但是你通过制作这些隐含的未包装的Optionals而抛弃了这种安全性。 这就是感叹号的含义;这意味着你可以崩溃。如果你不希望发生这种情况,那么:

  • 不要这样做。或者:

  • 自己插入nil的明确测试。