Swift if let 在 Optional(nil) 上成功评估

Posted

技术标签:

【中文标题】Swift if let 在 Optional(nil) 上成功评估【英文标题】:Swift if let evaluates successfully on Optional(nil) 【发布时间】:2014-09-16 08:11:19 【问题描述】:

我有一个名为 Field 的自定义对象。我基本上用它来定义表单中的单个字段。

class Field 

    var name: String
    var value: Any?

    // initializers here...

当用户提交表单时,我会验证每个 Field 对象以确保它们包含有效值。有些字段不是必需的,所以我有时故意将nil 设置为value 属性,如下所示:

field.value = nil

当我使用 if-let 来确定字段是否为 nil 时,这似乎会造成问题。

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.valuevalue

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

【问题讨论】:

无法复制。您使用哪个 Xcode 版本? Xcode 6 通用。尝试重新启动它,仍然没有运气。 :( 您能否发布一个完整的独立示例来演示该问题? 不要试图使用Any。通过使用Any,你放弃了一半的 Swift 特性(强类型)。为什么不在那里使用一个不错的泛型类型? @MattQuiros:所以使用具有两种情况的枚举:case CoreDataObject(NSManagedObject)case StringField(String),然后使用该枚举的可选类型而不是 Any? 【参考方案1】:

我相信这是因为Any? 允许任何值,而Optional.None 被解释为另一个值,因为Optional 是一个枚举!

AnyObject? 应该无法做到这一点,因为它只能包含Optional.Some([any class object]),这不允许Optional.Some(Optional) 的值Optional.None

这甚至说起来都令人深感困惑。重点是:尝试AnyObject? 而不是Any? 看看是否可行。


更重要的是,Matt 的一条评论提到,他想要使用 Any 的原因是为了选择可能是文本输入字段或旨在选择核心数据对象的字段。

在这种情况下,Swifty 要做的事情是使用enum with associated values,与tagged/discriminated union 基本相同。以下是如何声明、分配和使用此类枚举:

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? 中可能隐藏了哪些内容。有两种情况,并记录在案。当然,选择变量可以是可选的,不用担心。

【讨论】:

正确答案是“不惜一切代价避免Any”。如果你使用可选的Any,你就完蛋了。 是的,因为语言似乎没有办法区分 Optional.None 是用作实际值还是在可选 Any 中标记值的缺失。 【参考方案2】:

有一个提示可以用枚举替换我的 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)")

    

简而言之,我将 nil 值 (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

以上是关于Swift if let 在 Optional(nil) 上成功评估的主要内容,如果未能解决你的问题,请参考以下文章

Swift 1.2 中 UIImage 的 If Let vs Guard Let

如何理解 if let 与guard

Swift学习笔记之---使用if和let处理空变量

Swift学习笔记之---使用if和let处理空变量

Swift学习笔记之---使用if和let处理空变量

Swift学习笔记之---使用if和let处理空变量