Swift中如果let在Optional(nil)上成功评估

5

我有一个自定义对象叫做Field。基本上,我使用它来定义表单中的单个字段。

class Field {

    var name: String
    var value: Any?

    // initializers here...
}

当用户提交表单时,我会验证每个Field对象以确保它们包含有效的值。有些字段不是必需的,因此我有时会故意将value属性设置为nil,就像这样:
field.value = nil

当我使用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.
}

我在上面的if-else语句中设置了断点。当field.value故意被设置为nil时,它经过了if-let块,而不是else块。但是,对于我没有初始化和分配field.value的字段,程序进入else块。 我尝试在if-let块中打印出field.value和value:
if let value = field.value {
    NSLog("field.value: \(field.value), value: \(value)")
}

这是我得到的结果:

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

因此,我认为使用可选项,未初始化和值为nil可能是两个不同的概念。但是,在if-let内部添加另一个if也无法使编译器满意。
if let value = field.value {
    if value == nil { // Cannot invoke '==' with an argument list of type '(Any, NilLiteralConvertible)'
    }
}

我该怎么做才能解决这个问题?我只想检查field.value是否为nil


1
无法重现。您使用的Xcode版本是哪个? - Martin R
你能否发布一个完整的自包含示例来演示问题? - Martin R
2
不要被使用“Any”所诱惑。通过使用“Any”,您放弃了Swift功能的一半(强类型)。为什么不在那里使用一个好的泛型类型呢? - Sulthan
@MartinR 嗯,你说得对。我在新项目中无法再现它。我会看看我可能在哪里出了问题。参考代码:http://pastebin.com/BEJDXvYP - Matthew Quiros
1
@MattQuiros:所以使用一个有两个情况的枚举:case CoreDataObject(NSManagedObject)case StringField(String),然后使用该枚举的可选项作为类型,而不是 Any? - Jesper
显示剩余3条评论
2个回答

3
我认为这是因为Any?允许任何值,而Optional.None被解释为另一个值,因为Optional是一个枚举!AnyObject?无法做到这一点,因为它只能包含Optional.Some([any class object]),这不允许使用值Optional.None的情况下的Optional.Some(Optional)

即使谈论这个问题也会深感困惑。重点是:尝试使用AnyObject?而不是Any?,看看是否有效。


更重要的是,Matt的评论中提到他想使用Any的原因是选择可以是文本输入字段或用于选择Core Data对象的字段。

在这种情况下,使用带有关联值的枚举是Swifty的方法,基本上与标记/判别式联合体相同。以下是声明、分配和使用此类枚举的方法:

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
    }
}

使用这样的枚举类型,您不需要担心可能隐藏在Any?中的内容。它有两种情况并且已经记录了文档。当然,选择变量可以是可选的而不必担心任何问题。

1
正确答案是“不惜一切代价避免使用Any”。如果你正在使用可选的Any,那么你就完了。 - Sulthan
2
是的,因为似乎语言无法区分 Optional.None 是用作实际值还是用于标记可选 Any 中的值的缺失。 - Jesper

0

有一个小技巧是用枚举替换我的 Any? 类型,但我一直无法摆脱这个错误。更改我的方法并不会改变当前方法存在问题的事实。我必须弄清楚我如何得到一个Optional(nil)输出。

我通过在新的单视图项目中编写以下视图控制器来复现了错误。请注意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)")

    }
}

简而言之,我将一个空值(AppState().currentValue)传递给了一个接受Any的初始化器,并将其分配给类型为Any?的属性。有趣的是,如果我直接传递nil,编译器会抱怨:
let f = Field("Amount", nil) // Type 'Any' does not conform to protocol 'NilLiteralConvertible'

看起来在某个地方,Swift将AppState().currentValuenil值包装成了可选项,因此变成了Optional(nil)


nil并不存在于值中(只存在于关键字中),但是Optional.None是任何可选变量的默认值,并且在被要求描述自身时打印nil - Jesper

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接