很多时候,您需要编写如下代码:
if someOptional != nil {
// do something with the unwrapped someOptional e.g.
someFunction(someOptional!)
}
这似乎有点冗长,而且我听说使用!
force unwrap 运算符可能是不安全的,最好避免。有没有更好的方法来处理这个?
几乎总是没有必要检查一个可选项是否不是nil
。几乎你唯一需要这样做的时候是它的nil
-ness 是你唯一想知道的事情——你不在乎值是什么,只是它不是nil
。
在大多数其他情况下,有一点 Swift 速记可以更安全、更简洁地if
为你完成里面的任务。
如果不是,则使用该值nil
代替:
let s = "1"
let i = Int(s)
if i != nil {
print(i! + 1)
}
你可以使用if let
:
if let i = Int(s) {
print(i + 1)
}
您还可以使用var
:
if var i = Int(s) {
print(++i) // prints 2
}
但请注意,这i
将是一个本地副本 - 任何更改i
都不会影响原始可选中的值。
您可以在单个 中解开多个选项if let
,后面的选项可以依赖于前面的选项:
if let url = NSURL(string: urlString),
data = NSData(contentsOfURL: url),
image = UIImage(data: data)
{
let view = UIImageView(image: image)
// etc.
}
您还可以将where
子句添加到展开的值:
if let url = NSURL(string: urlString) where url.pathExtension == "png",
let data = NSData(contentsOfURL: url), image = UIImage(data: data)
{ etc. }
替换nil
为默认值
代替:
let j: Int
if i != nil {
j = i
}
else {
j = 0
}
或者:
let j = i != nil ? i! : 0
您可以使用 nil-coalescing 运算符??
:
// j will be the unwrapped value of i,
// or 0 if i is nil
let j = i ?? 0
将可选项与非可选项等同起来
代替:
if i != nil && i! == 2 {
print("i is two and not nil")
}
您可以检查选项是否等于非可选值:
if i == 2 {
print("i is two and not nil")
}
这也适用于比较:
if i < 5 { }
nil
总是等于其他nil
s,并且小于任何非nil
值。
当心!这里可能有陷阱:
let a: Any = "hello"
let b: Any = "goodbye"
if (a as? Double) == (b as? Double) {
print("these will be equal because both nil...")
}
在可选对象上调用方法(或读取属性)
代替:
let j: Int
if i != nil {
j = i.successor()
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
您可以使用可选链接,?.
:
let j = i?.successor()
请注意,j
现在也是可选的,以说明该fatalError
场景。稍后,您可以使用此答案中的其他技术之一来处理j
's 的可选项性,但您通常可以推迟实际展开您的可选项,直到很久以后,或者有时根本不展开。
顾名思义,您可以将它们链接起来,因此您可以编写:
let j = s.toInt()?.successor()?.successor()
可选链接也适用于下标:
let dictOfArrays: ["nine": [0,1,2,3,4,5,6,7]]
let sevenOfNine = dictOfArrays["nine"]?[7] // returns {Some 7}
和功能:
let dictOfFuncs: [String:(Int,Int)->Int] = [
"add":(+),
"subtract":(-)
]
dictOfFuncs["add"]?(1,1) // returns {Some 2}
分配给可选的属性
代替:
if splitViewController != nil {
splitViewController!.delegate = self
}
您可以通过可选链分配:
splitViewController?.delegate = self
只有当splitViewController
是非nil
时,分配才会发生。
如果不是nil
,则使用该值,或者放弃(Swift 2.0 中的新功能)
有时在一个函数中,你想编写一小段代码来检查一个可选项,如果是nil
,请提前退出函数,否则继续。
你可以这样写:
func f(s: String) {
let i = Int(s)
if i == nil { fatalError("Input must be a number") }
print(i! + 1)
}
或避免强制展开,如下所示:
func f(s: String) {
if let i = Int(s) {
print(i! + 1)
}
else {
fatalErrr("Input must be a number")
}
}
但是通过检查将错误处理代码保持在顶部会更好。这也可能导致令人不快的嵌套(“厄运金字塔”)。
相反,您可以使用guard
,这就像一个if not let
:
func f(s: String) {
guard let i = Int(s)
else { fatalError("Input must be a number") }
// i will be an non-optional Int
print(i+1)
}
该else
部分必须退出受保护值的范围,例如 areturn
或fatalError
,以保证受保护值对范围的其余部分有效。
guard
不限于功能范围。例如以下:
var a = ["0","1","foo","2"]
while !a.isEmpty {
guard let i = Int(a.removeLast())
else { continue }
print(i+1, appendNewline: false)
}
打印321
。
循环遍历序列中的非零项目(Swift 2.0 中的新功能)
如果您有一系列可选元素,则可以使用它for case let _?
来遍历所有非可选元素:
let a = ["0","1","foo","2"]
for case let i? in a.map({ Int($0)}) {
print(i+1, appendNewline: false)
}
打印321
。这是使用可选的模式匹配语法,它是一个变量名,后跟?
.
您还可以在switch
语句中使用此模式匹配:
func add(i: Int?, _ j: Int?) -> Int? {
switch (i,j) {
case (nil,nil), (_?,nil), (nil,_?):
return nil
case let (x?,y?):
return x + y
}
}
add(1,2) // 3
add(nil, 1) // nil
循环直到函数返回nil
很像if let
,您也可以编写while let
和循环直到nil
:
while let line = readLine() {
print(line)
}
你也可以写while var
(应用类似的警告if var
)。
where
子句也可以在这里工作(并终止循环,而不是跳过):
while let line = readLine()
where !line.isEmpty {
print(line)
}
将可选参数传递给一个接受非可选参数并返回结果的函数
代替:
let j: Int
if i != nil {
j = abs(i!)
}
else {
// no reasonable action to take at this point
fatalError("no idea what to do now...")
}
您可以使用可选的map
运算符:
let j = i.map { abs($0) }
这与可选链接非常相似,但是当您需要将非可选值作为参数传递给函数时。与可选链接一样,结果将是可选的。
当您无论如何都想要一个可选时,这很好。例如,reduce1
is like reduce
,但使用第一个值作为种子,如果数组为空,则返回一个可选值。你可以这样写(使用guard
前面的关键字):
extension Array {
func reduce1(combine: (T,T)->T)->T? {
guard let head = self.first
else { return nil }
return dropFirst(self).reduce(head, combine: combine)
}
}
[1,2,3].reduce1(+) // returns 6
但是,您可以map
使用该.first
属性,然后返回:
extension Array {
func reduce1(combine: (T,T)->T)->T? {
return self.first.map {
dropFirst(self).reduce($0, combine: combine)
}
}
}
将可选项传递给接受可选项并返回结果的函数,避免烦人的双重可选项
有时,你想要类似于 的东西map
,但你想调用的函数本身返回一个可选的。例如:
// an array of arrays
let arr = [[1,2,3],[4,5,6]]
// .first returns an optional of the first element of the array
// (optional because the array could be empty, in which case it's nil)
let fst = arr.first // fst is now [Int]?, an optional array of ints
// now, if we want to find the index of the value 2, we could use map and find
let idx = fst.map { find($0, 2) }
但是 nowidx
是 type Int??
,一个双重可选的。相反,您可以使用flatMap
,它将结果“扁平化”为单个可选:
let idx = fst.flatMap { find($0, 2) }
// idx will be of type Int?
// and not Int?? unlike if `map` was used
我认为你应该回到 Swift 编程书,了解这些东西的用途。!当您绝对确定可选项不为零时使用。既然你声明你绝对确定,如果你错了它就会崩溃。这完全是故意的。从代码中的断言“不安全且最好避免”的意义上说,它是“不安全且最好避免的”。例如:
if someOptional != nil {
someFunction(someOptional!)
}
这 !绝对安全。除非你的代码有很大的错误,比如写错了(我希望你能发现这个错误)
if someOptional != nil {
someFunction(SomeOptional!)
}
在这种情况下,您的应用程序可能会崩溃,您需要调查它崩溃的原因,然后修复错误——这正是崩溃的原因。Swift 的一个目标显然是您的应用程序应该正常运行,但由于 Swift 无法强制执行此操作,它会强制您的应用程序要么正常运行,要么在可能的情况下崩溃,因此在应用程序发布之前删除错误。
你有一种方法。它被称为可选链。从文档:
可选链是查询和调用当前可能为 nil 的可选的属性、方法和下标的过程。如果可选包含值,则属性、方法或下标调用成功;如果可选项为 nil,则属性、方法或下标调用返回 nil。多个查询可以链接在一起,如果链中的任何链接为零,则整个链都会优雅地失败。
这是一些例子
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
你可以在这里查看全文。
我们可以使用可选绑定。
var x:Int?
if let y = x {
// x was not nil, and its value is now stored in y
}
else {
// x was nil
}
经过大量的思考和研究,我想出了最简单的方法来打开一个可选的:
创建一个新的 Swift 文件并将其命名为 UnwrapOperator.swift
将以下代码粘贴到文件中:
import Foundation
import UIKit
protocol OptionalType { init() }
extension String: OptionalType {}
extension Int: OptionalType {}
extension Int64: OptionalType {}
extension Float: OptionalType {}
extension Double: OptionalType {}
extension CGFloat: OptionalType {}
extension Bool: OptionalType {}
extension UIImage : OptionalType {}
extension IndexPath : OptionalType {}
extension NSNumber : OptionalType {}
extension Date : OptionalType {}
extension UIViewController : OptionalType {}
postfix operator *?
postfix func *?<T: OptionalType>( lhs: T?) -> T {
guard let validLhs = lhs else { return T() }
return validLhs
}
prefix operator /
prefix func /<T: OptionalType>( rhs: T?) -> T {
guard let validRhs = rhs else { return T() }
return validRhs
}
现在上面的代码已经创建了2个运算符[一个前缀和一个后缀]。
解释很简单,如果运算符在变量中获得 nil 则返回构造函数值,否则返回变量中包含的值。
以下是使用示例:
var a_optional : String? = "abc"
var b_optional : Int? = 123
// before the usage of Operators
print(a_optional) --> Optional("abc")
print(b_optional) --> Optional(123)
// Prefix Operator Usage
print(/a_optional) --> "abc"
print(/b_optional) --> 123
// Postfix Operator Usage
print(a_optional*?) --> "abc"
print(b_optional*?) --> 123
以下是变量包含 nil时的示例:
var a_optional : String? = nil
var b_optional : Int? = nil
// before the usage of Operators
print(a_optional) --> nil
print(b_optional) --> nil
// Prefix Operator Usage
print(/a_optional) --> ""
print(/b_optional) --> 0
// Postfix Operator Usage
print(a_optional*?) --> ""
print(b_optional*?) --> 0
现在,您可以选择使用哪个运算符,两者的目的相同。