简体   繁体   English

Swift如果让我们成功评估Optional(nil)

[英]Swift if let evaluates successfully on Optional(nil)

I have a custom object called Field. 我有一个名为Field的自定义对象。 I basically use it to define a single field in a form. 我基本上用它来定义表单中的单个字段。

class Field {

    var name: String
    var value: Any?

    // initializers here...
}

When the user submits the form, I validate each of the Field objects to make sure they contain valid values. 当用户提交表单时,我验证每个Field对象以确保它们包含有效值。 Some fields aren't required so I sometimes deliberately set nil to the value property like this: 有些字段不是必需的,所以我有时会故意将nil设置为value属性,如下所示:

field.value = nil

This seems to pose a problem when I use an if-let to determine whether a field is nil or not. 当我使用if-let来确定字段是否为零时,这似乎会造成问题。

if let value = field.value {
    // The field has a value, ignore it...
} else {
    // Add field.name to the missing fields array. Later, show the
    // missing fields in a dialog.
}

I set breakpoints in the above if-else and when field.value has been deliberately set to nil, it goes through the if-let block, not the else. 我在上面的if-else中设置了断点, field.value被故意设置为nil时,它会通过if-let块,而不是else。 However, for the fields whose field.value I left uninitialized and unassigned, the program goes to the else block. 但是,对于field.value我保持未初始化和未分配的字段, 程序将转到else块。

I tried printing out field.value and value inside the if-let block: 我尝试在if-let块中打印出field.valuevalue

if let value = field.value {
    NSLog("field.value: \(field.value), value: \(value)")
}

And this is what I get: 这就是我得到的:

field.value: Optional(nil), value: nil field.value:可选(零),值:nil

So I thought that maybe with optionals, it's one thing to be uninitialized and another to have the value of nil. 所以我认为可能有选择性,未初始化是一回事而另一个是nil的价值。 But even adding another if inside the if-let won't make the compiler happy: 但即使在if-let中添加另一个也不会让编译器满意:

if let value = field.value {
    if value == nil { // Cannot invoke '==' with an argument list of type '(Any, NilLiteralConvertible)'
    }
}

How do I get around this? 我该如何解决这个问题? I just want to check if the field.value is nil . 我只是想检查field.value是否nil

I believe this is because Any? 我相信这是因为Any? allows any value and Optional.None is being interpreted as just another value, since Optional is an enum! 允许任何值和Optional.None被解释为另一个值,因为Optional是一个枚举!

AnyObject? should be unable to do this since it only can contain Optional.Some([any class object]) , which does not allow for the case Optional.Some(Optional) with the value Optional.None . 应该无法执行此操作,因为它只能包含Optional.Some([any class object]) ,这不允许使用值Optional.None Optional.Some(Optional)大小写。

This is deeply confusing to even talk about. 甚至谈论这都令人深感困惑。 The point is: try AnyObject? 关键是:尝试AnyObject? instead of Any? 而不是Any? and see if that works. 并看看是否有效。


More to the point, one of Matt's comment mentions that the reason he wants to use Any is for a selection that could be either a field for text input or a field intended to select a Core Data object. 更重要的是,Matt的评论之一提到他想要使用Any的原因是选择可以是文本输入字段或用于选择Core Data对象的字段。

The Swifty thing to do in this case is to use an enum with associated values , basically the same thing as a tagged/discriminated union . 在这种情况下要做的Swifty事情是使用带有关联值枚举 ,基本上与标记/区分联合相同 Here's how to declare, assign and use such an enum: 以下是如何声明,分配和使用这样的枚举:

enum DataSelection {
    case CoreDataObject(NSManagedObject)
    case StringField(String)
}

var selection : DataSelection?

selection = .CoreDataObject(someManagedObject)

if let sel = selection { // if there's a selection at all
    switch sel {
        case .CoreDataObject(let coreDataObj):
            // do what you will with coreDataObj
        case .StringField(let string):
            // do what you will with string
    }
}

Using an enum like this, there's no need to worry about which things could be hiding inside that Any? 使用这样的枚举,没有必要担心哪些内容可能隐藏在Any? . There are two cases and they are documented. 有两种情况,它们都有记录。 And of course, the selection variable can be an optional without any worries. 当然,选择变量可以是可选的而不用担心。

There's a tip to replace my Any? 有一个提示可以替换我的Any? type with an enum but I couldn't get this error out of my head. 用枚举键入,但我无法从头脑中得到这个错误。 Changing my approach doesn't change the fact that something is wrong with my current one and I had to figure out how I arrived at an Optional(nil) output. 改变我的方法并没有改变我当前的方法出错的事实,我不得不弄清楚我是如何得到一个Optional(nil)输出。

I was able to reproduce the error by writing the following view controller in a new single-view project. 通过在新的单视图项目中编写以下视图控制器,我能够重现错误。 Notice the init signature. 注意init签名。

import UIKit

class Field {
    var name: String = "Name"
    var value: Any?

    init(_ name: String, _ value: Any) {
        self.name = name
        self.value = value
    }
}

class AppState {
    var currentValue: Field?
}

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let f = Field("Amount", AppState().currentValue)
        NSLog("\(f.value)")

    }
}

In short, I was passing a nil value ( AppState().currentValue ) to an initializer that accepts Any , and assigns it to a property whose type is Any? 简而言之,我将nil值( AppState().currentValue )传递给接受Any的初始值设定项,并将其分配给类型为Any?的属性Any? . The funny thing here is if I directly passed nil instead, the compiler will complain: 这里有趣的是,如果我直接传递nil ,编译器会抱怨:

let f = Field("Amount", nil) // Type 'Any' does not conform to protocol 'NilLiteralConvertible'

It seems that somewhere along the way, Swift wraps the nil value of AppState().currentValue in an optional, hence Optional(nil) . 似乎在某个地方,Swift将AppState().currentValuenil值包含在一个可选的,因此是Optional(nil)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM