首页 文章

Swift中的可选值是什么?

提问于
浏览
240

来自Apple's documentation

您可以使用if和let一起处理可能缺失的值 . 这些值表示为选项 . 可选值包含值或包含nil以指示缺少值 . 在值的类型后面写一个问号(?)以将值标记为可选 .

为什么要使用可选值?

12 回答

  • -1

    Swift中的可选项是一种可以保存值或不保存值的类型 . 通过将 ? 附加到任何类型来编写选项:

    var name: String? = "Bertie"
    

    Optionals(以及Generics)是最难理解的Swift概念之一 . 由于它们的编写和使用方式,很容易弄清楚它们是什么 . 将上面的可选项与创建普通String进行比较:

    var name: String = "Bertie" // No "?" after String
    

    从语法看起来,可选的String与普通的String非常相似 . 不是 . 可选的String不是打开某些“可选”设置的String . 它不是一种特殊的String . String和可选String是完全不同的类型 .

    这是最重要的事情:可选是一种容器 . 可选的String是一个可能包含String的容器 . 可选的Int是一个可能包含Int的容器 . 将可选项视为一种包裹 . 在打开它之前(或用选项语言“展开”),你不会知道它是否包含任何东西 .

    通过在任何Swift文件中键入"Optional"并⌘-单击它,可以在Swift标准库中看到how optionals are implemented . 这是定义的重要部分:

    enum Optional<Wrapped> {
        case none
        case some(Wrapped)
    }
    

    可选只是 enum ,可以是以下两种情况之一: .none.some . 如果它是 .some ,则存在一个关联值,在上面的示例中,它将是 String "Hello" . 可选项使用泛型为相关值提供类型 . 可选字符串的类型不是 String ,它是 Optional ,或者更确切地说是 Optional<String> .

    Swift对可选项所做的一切都是神奇的,使得阅读和编写代码更加流畅 . 不幸的是,这掩盖了它实际工作的方式 . 我稍后会介绍一些技巧 .

    Note: 我'll be talking about optional variables a lot, but it'很好也可以创建可选常量 . 我用它们的类型标记所有变量,以便更容易理解正在创建的类型类型,但您不必在自己的代码中 .


    如何创建选项

    要创建可选项,请在要包装的类型后附加 ? . 任何类型都是可选的,甚至是您自己的自定义类型 . 您不能在类型和 ? 之间留出空格 .

    var name: String? = "Bob" // Create an optional String that contains "Bob"
    var peter: Person? = Person() // An optional "Person" (custom type)
    
    // A class with a String and an optional String property
    class Car {
    var modelName: String // must exist
    var internalName: String? // may or may not exist
    }
    

    使用选项

    您可以将可选项与 nil 进行比较,以查看它是否具有值:

    var name: String? = "Bob"
    name = nil // Set name to nil, the absence of a value
    if name != nil {
        print("There is a name")
    }
    if name == nil { // Could also use an "else"
        print("Name has no value")
    }
    

    这有点令人困惑 . 这意味着可选的是一件事或另一件事 . 它's either nil or it' s "Bob" . 事实并非如此,可选项不会转换为其他内容 . 将它与nil进行比较是制作易于阅读的代码的一种技巧 . 如果可选等于nil,这只意味着枚举当前设置为 .none .


    只有选项可以是零

    如果您尝试将非可选变量设置为nil,则会出现错误 .

    var red: String = "Red"
    red = nil // error: nil cannot be assigned to type 'String'
    

    查看选项的另一种方法是作为普通Swift变量的补充 . 它们是保证具有值的变量的对应物 . 斯威夫特是一种讨厌模糊的语言 . 大多数变量都被定义为非选项,但有时这并不能保证图像变量的值 . 在这种情况下,您必须使其成为可选项 . 它从 nil 开始,当检索到图像时,可选项获取一个值 .

    使用可选项显示程序员的意图 . 与Objective-C相比,任何对象都可以为零,Swift需要您清楚何时可以丢失值以及何时保证存在 .


    要使用可选项,您可以“解开”它

    可选的 String 不能用于代替实际的 String . 要在可选内部使用包装值,您必须打开它 . 解包可选项的最简单方法是在可选名称后添加 ! . 这叫做"force unwrapping" . 它返回可选内部的值(作为原始类型),但如果可选项是 nil ,则会导致运行时崩溃 . 打开之前你应该确定它有 Value .

    var name: String? = "Bob"
    let unwrappedName: String = name!
    print("Unwrapped name: \(unwrappedName)")
    
    name = nil
    let nilName: String = name! // Runtime crash. Unexpected nil.
    

    检查并使用可选项

    因为在打开之前你应该总是检查nil并使用可选项,这是一种常见的模式:

    var mealPreference: String? = "Vegetarian"
    if mealPreference != nil {
        let unwrappedMealPreference: String = mealPreference!
        print("Meal: \(unwrappedMealPreference)") // or do something useful
    }
    

    在这种模式中,您检查是否存在值,然后当您确定它是时,您强制将其打包到临时常量中以供使用 . 因为这是一件很常见的事情,Swift提供了一个使用“if let”的快捷方式 . 这称为“可选绑定” .

    var mealPreference: String? = "Vegetarian"
    if let unwrappedMealPreference: String = mealPreference {
        print("Meal: \(unwrappedMealPreference)") 
    }
    

    这会创建一个临时常量(如果用 var 替换 let ,则为变量),其范围仅在if的大括号内 . 因为必须使用像"unwrappedMealPreference"或"realMealPreference"这样的名称是一种负担,Swift允许您重用原始变量名称,在括号范围内创建一个临时变量名称

    var mealPreference: String? = "Vegetarian"
    if let mealPreference: String = mealPreference {
        print("Meal: \(mealPreference)") // separate from the other mealPreference
    }
    

    这里有一些代码来演示使用不同的变量:

    var mealPreference: String? = "Vegetarian"
    if var mealPreference: String = mealPreference {
        print("Meal: \(mealPreference)") // mealPreference is a String, not a String?
        mealPreference = "Beef" // No effect on original
    }
    // This is the original mealPreference
    print("Meal: \(mealPreference)") // Prints "Meal: Optional("Vegetarian")"
    

    可选绑定通过检查可选等于nil来工作 . 如果它没有让它沉默:

    print("\(mealPreference.debugDescription)")
    

    什么是期权?

    Optionals有两个用例:

    • 可能失败的事情(我期待的东西,但我一无所获)

    • 现在什么都不是,但后来可能会出现的事情(反之亦然)

    一些具体的例子:

    • 可以存在或不存在的属性,如 Person 类中的 middleNamespouse

    • 一种可以返回值或不返回值的方法,例如在数组中搜索匹配项

    • 一种方法,它可以返回结果或获取错误并且不返回任何内容,例如尝试读取文件's contents (which normally returns the file' s数据)但该文件不存在

    • 委托属性,不一定要设置,通常在初始化后设置

    • 对于类中的 weak 属性 . 他们指向的东西可以随时设置为 nil

    • 可能必须释放以回收内存的大型资源

    • 当您需要一种方法来知道何时设置了一个值(数据尚未加载>数据)而不是使用单独的dataLoaded Boolean

    Objective-C中不存在选项,但是有一个等价的概念,返回nil . 可以返回对象的方法可以返回nil . 这被认为意味着"the absence of a valid object"并经常被用来说出错了 . 它仅适用于Objective-C对象,而不适用于基元或基本C类型(枚举,结构) . Objective-C通常有专门的类型来表示缺少这些值( NSNotFound ,它实际上是 NSIntegerMaxkCLLocationCoordinate2DInvalid 表示无效坐标, -1 或某些负值也被使用) . 编码人员必须了解这些特殊值,因此必须记录并学习每种情况 . 如果方法不能将 nil 作为参数,则必须记录 . 在Objective-C中, nil 是一个指针,就像所有对象都被定义为指针一样,但是 nil 指向一个特定的(零)地址 . 在Swift中, nil 是一个文字,表示缺少某种类型 .


    比较为零

    您曾经能够使用任何可选的 Boolean

    let leatherTrim: CarExtras? = nil
    if leatherTrim {
        price = price + 1000
    }
    

    在更新版本的Swift中,您必须使用 leatherTrim != nil . 为什么是这样?问题是 Boolean 可以包装在一个可选项中 . 如果您有这样的 Boolean

    var ambiguous: Boolean? = false
    

    它有两种"false",一种没有值,另一种有值,但值为 false . Swift讨厌歧义现在你必须经常检查 nil 的可选项 .

    您可能想知道可选 Boolean 的重点是什么?与其他期权一样, .none 状态可能表明该值尚未知 . 网络呼叫的另一端可能有某些东西需要一些时间来轮询 . 可选的布尔也被称为“Three-Value Booleans


    Swift技巧

    Swift使用一些技巧来允许选项工作 . 考虑这三行普通可选代码;

    var religiousAffiliation: String? = "Rastafarian"
    religiousAffiliation = nil
    if religiousAffiliation != nil { ... }
    

    这些行都不应该编译 .

    • 第一行使用String文字设置可选的String,两种不同的类型 . 即使这是 String ,类型也不同

    • 第二行将可选String设置为nil,两种不同的类型

    • 第三行将可选字符串与nil,两种不同类型进行比较

    我将介绍允许这些行工作的选项的一些实现细节 .


    创建可选项

    使用 ? 创建一个可选的语法糖,由Swift编译器启用 . 如果你想做很长的事,你可以创建一个像这样的可选项:

    var name: Optional<String> = Optional("Bob")
    

    这称为 Optional 的第一个初始化程序, public init(_ some: Wrapped) ,它从括号内使用的类型推断出可选的关联类型 .

    创建和设置可选项的更长方法:

    var serialNumber:String? = Optional.none
    serialNumber = Optional.some("1234")
    print("\(serialNumber.debugDescription)")
    

    将可选设置为nil

    您可以创建一个没有初始值的可选项,或者创建一个初始值为 nil 的可选项(两者都具有相同的结果) .

    var name: String?
    var name: String? = nil
    

    协议 ExpressibleByNilLiteral (以前称为 NilLiteralConvertible )启用允许等于 nil 的选项 . 使用 Optional 的第二个初始值设定项 public init(nilLiteral: ()) 创建了可选项 . 文档说你不应该将 ExpressibleByNilLiteral 用于除选项之外的任何东西,因为这会改变代码中nil的含义,但是可以这样做:

    class Clint: ExpressibleByNilLiteral {
        var name: String?
        required init(nilLiteral: ()) {
            name = "The Man with No Name"
        }
    }
    
    let clint: Clint = nil // Would normally give an error
    print("\(clint.name)")
    

    相同的协议允许您将已创建的可选项设置为 nil . 虽然不推荐,但您可以直接使用nil literal初始化程序:

    var name: Optional<String> = Optional(nilLiteral: ())
    

    将可选项与nil进行比较

    Optionals定义了两个特殊的"=="和"!="运算符,您可以在 Optional 定义中看到它们 . 第一个 == 允许您检查是否有任何可选项等于nil . 如果关联类型相同,则设置为.none的两个不同选项将始终相等 . 当你比较nil时,在后面Swift创建一个相同关联类型的可选项,设置为.none然后使用它进行比较 .

    // How Swift actually compares to nil
    var tuxedoRequired: String? = nil
    let temp: Optional<String> = Optional.none
    if tuxedoRequired == temp { // equivalent to if tuxedoRequired == nil
        print("tuxedoRequired is nil")
    }
    

    第二个 == 运算符允许您比较两个选项 . 两者都必须是相同的类型,并且该类型需要符合 Equatable (允许与常规"=="运算符进行比较的协议) . Swift(大概)解开这两个值并直接比较它们 . 它还处理一个或两个选项都是 .none 的情况 . 注意与 nil 文字比较之间的区别 .

    此外,它允许您将任何 Equatable 类型与可选包装进行比较:

    let numberToFind: Int = 23
    let numberFromString: Int? = Int("23") // Optional(23)
    if numberToFind == numberFromString {
        print("It's a match!") // Prints "It's a match!"
    }
    

    在幕后,Swift在比较之前将非可选项作为可选项包装 . 它也适用于文字( if 23 == numberFromString {

    我说有两个 == 运算符,但实际上有三分之一允许你把 nil 放在比较的左边

    if nil == name { ... }
    

    命名选项

    命名可选类型与非可选类型不同,没有Swift约定 . 人们避免在名称中添加内容以显示它是可选的(如“optionalMiddleName”或“possibleNumberAsString”),并让声明显示它是可选类型 . 当你想要命名某些东西以保存可选项中的值时,这会变得很困难 . 名称“middleName”意味着它是一个String类型,因此当您从中提取String值时,通常最终会得到“actualMiddleName”或“unwrappedMiddleName”或“realMiddleName”等名称 . 使用可选绑定并重用变量名来解决此问题 .


    官方定义

    来自"The Basics" in the Swift Programming Language

    Swift还引入了可选类型,它们处理缺少值 . Optionals说“有一个值,它等于x”或“根本没有值” . Optionals类似于在Objective-C中使用带有指针的nil,但它们适用于任何类型,而不仅仅是类 . Option-C中的可选项比nil指针更安全,更具表现力,是Swift最强大功能的核心 . 可选项是Swift是一种类型安全语言这一事实的一个例子 . Swift可以帮助您清楚代码可以使用的值的类型 . 如果您的部分代码需要String,则类型安全性会阻止您错误地将其传递给Int . 这使您能够在开发过程中尽早捕获并修复错误 .


    最后,这是1899年关于选项的一首诗:

    昨天在楼梯上
    我遇到了一个不在那里的男人
    他今天不在了
    我希望,我希望他能离开
    Antigonish


    更多资源:

  • 0

    让我们以 NSError 为例,如果没有't an error being returned you' d想让它可选择返回Nil . 有's no point in assigning a value to it if there isn' t错误..

    var error: NSError? = nil
    

    这也允许您拥有默认值 . 因此,如果函数未传递任何内容,则可以将方法设置为默认值

    func doesntEnterNumber(x: Int? = 5) -> Bool {
        if (x == 5){
            return true
        } else {
            return false
        }
    }
    
  • 1

    你不能在Swift中有一个指向 nil 的变量 - 没有指针,也没有空指针 . 但是在API中,您通常希望能够指明具体的 Value ,或缺乏 Value - 例如我的窗口有一个代表,如果有,那是谁?可选项是Swift的类型安全,内存安全的方法 .

  • 0

    我做了一个简短的回答,总结了上面的大部分内容,以清除我作为初学者的不确定性:

    与Objective-C相反,Swift中没有变量可以包含 nil ,因此添加了Optional变量类型(变量后缀为"?"):

    var aString = nil //error
    

    最大的区别是Optional变量不直接存储值(正常的Obj-C变量)它们包含 two states :“_

    var nhw : String? = "Hello World"
    
    //This is correct
    
    if nhw
    
     {..}
    

    " or " has nil ”:

    var aString: String? = "Hello, World!"
        aString = nil //correct, now it contains the state "has nil"
    

    也就是说,您可以在不同情况下检查这些变量:

    if let myString = aString? {
         println(myString)
    }
    else { 
         println("It's nil") // this will print in our case
    }
    

    通过使用"!"后缀,您还可以访问包含在其中的值 only if those exist . (即它不是 nil ):

    let aString: String? = "Hello, World!"
    // var anotherString: String = aString //error
    var anotherString: String = aString!
    
    println(anotherString) //it will print "Hello, World!"
    

    这就是你需要使用“?”的原因 . 和“!”并且默认情况下不使用所有这些 . (这是我最大的困惑)

    我也同意上面的答案:可选类型不能用作布尔值 .

  • 7

    在目标C中,没有值的变量等于'nil'(也可以使用与n和false相同的'nil'值),因此可以在条件语句中使用变量(具有值的变量与'TRUE'相同'和那些没有 Value 观的人等于'假') .

    Swift通过提供“可选值”来提供类型安全性 . 即它可以防止因分配不同类型的变量而形成的错误 .

    所以在Swift中,只能在条件语句中提供布尔值 .

    var hw = "Hello World"
    

    在这里,偶数'虽然'hw'是一个字符串,但它不能在if语句中使用,就像在Objective C中一样 .

    //This is an error
    
    if hw
    
     {..}
    

    为此,它需要创建为,

    var nhw : String? = "Hello World"
    
    //This is correct
    
    if nhw
    
     {..}
    
  • 11

    可选值允许您显示缺少值 . 有点像SQL中的NULL或Objective-C中的NSNull . 我想这将是一个改进,因为即使是“原始”类型也可以使用它 .

    // Reimplement the Swift standard library's optional type
    enum OptionalValue<T> {
        case None
        case Some(T)
    }
    var possibleInteger: OptionalValue<Int> = .None
    possibleInteger = .Some(100)”
    

    摘录自:Apple Inc.“The Swift Programming Language . ”iBooks . https://itun.es/gb/jEUH0.l

  • 3

    可选的意思是Swift不完全确定该值是否与类型相对应:例如,Int?意味着Swift并不完全确定该数字是否为Int .

    要删除它,您可以使用三种方法 .

    1)如果您完全确定类型,可以使用感叹号强行打开它,如下所示:

    // Here is an optional variable:
    
    var age: Int?
    
    // Here is how you would force unwrap it:
    
    var unwrappedAge = age!
    

    如果您强行打开一个可选项并且它等于nil,则可能会遇到此崩溃错误:

    enter image description here

    这不一定是安全的,所以这里有一个方法可以防止崩溃,以防你不确定类型和值:

    方法2和3防止这个问题 .

    2)隐式解包可选

    if let unwrappedAge = age {
    
     // continue in here
    
     }
    

    注意,unwrapped类型现在是Int,而不是Int? .

    3)警卫声明

    guard let unwrappedAge = age else { 
       // continue in here
     }
    

    从这里开始,您可以继续使用未包装的变量 . 如果您确定变量的类型,请确保仅强制打开(使用!) .

    祝你的项目好运!

  • 476

    When i started to learn Swift it was very difficult to realize why optional .

    让我们以这种方式思考 . 让我们考虑一个 Person 类,它有两个属性 namecompany .

    class Person: NSObject {
    
        var name : String //Person must have a value so its no marked as optional
        var companyName : String? ///Company is optional as a person can be unemployed that is nil value is possible
    
        init(name:String,company:String?) {
    
            self.name = name
            self.companyName = company
    
        }
    }
    

    现在让我们创建一些 Person 的对象

    var tom:Person = Person.init(name: "Tom", company: "Apple")//posible
    var bob:Person = Person.init(name: "Bob", company:nil) // also Possible because company is marked as optional so we can give Nil
    

    但是我们无法将 Nil 传递给 name

    var personWithNoName:Person = Person.init(name: nil, company: nil)
    

    现在让我们谈谈为什么我们使用 optional? . 让我们考虑一下我们想在公司名称之后添加 Inc 的情况,例如 apple 将是 apple Inc . 我们需要在公司名称和印刷品后追加 Inc .

    print(tom.companyName+" Inc") ///Error saying optional is not unwrapped.
    print(tom.companyName!+" Inc") ///Error Gone..we have forcefully unwrap it which is wrong approach..Will look in Next line
    print(bob.companyName!+" Inc") ///Crash!!!because bob has no company and nil can be unwrapped.
    

    现在让我们研究一下为什么可选的选择 .

    if let companyString:String = bob.companyName{///Compiler safely unwrap company if not nil.If nil,no unwrap.
    
        print(companyString+" Inc") //Will never executed and no crash!!!
    }
    

    让我们用 tom 替换 bob

    if let companyString:String = tom.companyName{///Compiler safely unwrap company if not nil.If nil,no unwrap.
    
        print(companyString+" Inc") //Will never executed and no crash!!!
    }
    

    并且 Congratulation! 我们已正确处理 optional?

    所以实现点是

    • 我们将变量标记为可选,如果它可能是 nil

    • 如果我们想在代码编译器中的某个地方使用这个变量,它会提醒你,如果它包含 nil ,我们需要检查是否正确处理了该变量 .

    谢谢......快乐的编码

  • 14

    让我们试试下面的代码 Playground . 我希望能够清楚地知道什么是可选的以及使用它的原因 .

    var sampleString: String? ///Optional, Possible to be nil
    
    sampleString = nil ////perfactly valid as its optional
    
    sampleString = "some value"  //Will hold the value
    
    if let value = sampleString{ /// the sampleString is placed into value with auto force upwraped.
    
        print(value+value)  ////Sample String merged into Two
    }
    
    sampleString = nil // value is nil and the
    
    if let value = sampleString{
    
        print(value + value)  ///Will Not execute and safe for nil checking
    }
    
    //   print(sampleString! + sampleString!)  //this line Will crash as + operator can not add nil
    
  • 0

    来自https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html

    可选链接是一个查询和调用当前可能为nil的可选项的属性,方法和下标的过程 . 如果optional包含值,则属性,方法或下标调用成功;如果optional是nil,则属性,方法或下标调用返回nil . 多个查询可以链接在一起,如果有任何链接,整个链都会正常失败链是零 .

    要更深入地了解,请阅读上面的链接 .

  • 5

    好...

    ? (可选)表示您的变量可能包含零值! (unwrapper)表示您的变量在运行时使用(尝试从中获取值)时必须具有内存(或值) .

    主要区别在于,当optional是nil时,可选链接会正常失败,而当optional可选为nil时,强制解包会触发运行时错误 .

    为了反映可以在nil值上调用可选链接的事实,可选链接调用的结果始终是可选值,即使要查询的属性,方法或下标返回非可选值 . 您可以使用此可选返回值来检查可选链接调用是否成功(返回的可选项包含值),或者由于链中的nil值(返回的可选值为nil)而未成功 .

    具体来说,可选链接调用的结果与预期返回值的类型相同,但包含在可选中 . 通常返回Int的属性将返回Int?通过可选链接访问时 .

    var defaultNil : Int?  // declared variable with default nil value
    println(defaultNil) >> nil  
    
    var canBeNil : Int? = 4
    println(canBeNil) >> optional(4)
    
    canBeNil = nil
    println(canBeNil) >> nil
    
    println(canBeNil!) >> // Here nil optional variable is being unwrapped using ! mark (symbol), that will show runtime error. Because a nil optional is being tried to get value using unwrapper
    
    var canNotBeNil : Int! = 4
    print(canNotBeNil) >> 4
    
    var cantBeNil : Int = 4
    cantBeNil = nil // can't do this as it's not optional and show a compile time error
    

    以下是Apple开发者委员会的详细基础教程:Optional Chaining

  • 12

    这是Swift中的等效可选声明:

    var middleName: String?
    

    此声明创建一个名为middleName的String类型的变量 . String变量类型后面的问号(?)表示middleName变量可以包含一个可以是String或nil的值 . 任何查看此代码的人都会立即知道middleName可以为零 . 它是自我记录的!

    如果未指定可选常量或变量的初始值(如上所示),则该值将自动设置为nil . 如果您愿意,可以将初始值显式设置为nil:

    var middleName: String? = nil
    

    有关可选阅读下面链接的更多详细信息

    http://www.iphonelife.com/blog/31369/swift-101-working-swifts-new-optional-values

相关问题