关于Swift可选内容的一切

Swift带来了许多不是Objective-C一部分的新功能。 这些功能之一是Optionals 。 虽然Objective-C提供了一种将变量标记为nullable ,但是Swift在全新的水平上实现了可选方法。 在深入研究细节之前,让我们首先讨论为什么首先需要它们。 数据是任何软件的关键部分。 在现实世界中,并非始终保证数据可用性。 例如,由于某些网络问题,对API的调用可能会失败。 作为开发人员,您需要以友好的方式处理此类情况。 换句话说,有时,我们有时会拥有或可能没有有效数据。 这就是可选的背后的核心思想。 让我们详细讨论它。

在Swift中,可选选项可让我们处理可能具有合法值或没有合法值的情况。 乍一看,这似乎很明显,因为在许多其他语言中,常规数据持有者会遇到这种情况。 例如在Java中:

 字符串消息=“你好,世界!”; 
System.out.print(str); // “你好,世界!”

//可以将null / nil分配给常规字符串持有者
message = null;
System.out.print(message); // 空值

在Swift中,这是非常不同的。 无法将常规Swift变量分配为nil 。 因此,要表达缺少值,您需要使用可选。 实际上,这是您唯一可以在Swift中分配nil东西。 要将数据成员声明为可选,需要添加? 在数据类型之后。

  var id:Int 
id = nil //编译errorvar optionalId:Int? //可选的Int
optionalID = nil //合法的Swift表达式

这是有道理的,因为如果甚至有几分之一的数据丢失的可能性,您就需要在那里表达它。 许多开发人员不时犯下的一个常见错误是,以一种脆弱的方式访问变量,该变量没有任何合法的值,并最终导致运行时崩溃。 使用可选选项时,它会在编译时处理。 可以将Optional看作是强类型容器,它包含一个值或否则为nil 。 如果它包含一个值,则可以解开容器并使用基础值。 为此,您需要使用一些额外的信息,这些信息确定如何解开基础值。 让我们详细研究每个。

就像术语所说的那样,您强行打开容器并尝试获得实际值。 如果有一个,则获取该值;否则,将导致运行时崩溃。 安全的一面是当您百分百确定潜在价值的存在时使用此方法。 例如,如果声明了一个可选参数并立即赋予其初始值,则现在可以安全地将其拆开。 另外,您可以通过对nil进行检查来检查可选值是否包含值。 例如

  //可选的Int 
var randomNumber:Int?randomNumber = Int(arc4random_uniform(10))//强制展开
打印(randomNumber!)//安全检查
如果randomNumber!= nil {
打印(randomNumber!)
}

但是,它仍然是社区中与可选选项一起使用的最不受欢迎的方法。 实际上,这被认为是将代码气味引入代码库的原因之一。 有一种更好的方法来解开可选对象:可选绑定。

在这种方法中,我们创建一个临时常量,该常量只能在if语句的花括号内访问。 在这里,我们用一块石头打了两只鸟。 首先,检查一个可选参数是否为nil 。 如果不是nil ,则将其展开并为常数分配该值。 在花括号内,我们可以像使用常规数据一样使用此常量。

 让myOptional:加倍?  = 16如果让温度= myOptional { 
//温度是Double类型的,不是Optional Double
打印(温度)// 16
}
//这里不存在温度

现在,此方法的一个重要优点是,您不必强行打开可选组件。 您可能会问: “但是我们可以使用if语句来检查nil是否为可选 。” 是的你可以。 但是,仍然是强制展开。 考虑一个通过nil检查但在强制展开之前的竞争条件,应用程序的另一部分将nil分配给了可选参数。 在可选绑定中,您可以在单个语句中安全地检查和解包。

使用问号进行可选基本上是一种语法糖。 在幕后,我们还提供了另一种语言功能来支持可选功能。 如果我们仔细研究一下,则有两种情况可选(有价或无价)。 说到案例,它是枚举的不错选择。 这正是在Swift中实现Optionals的方式。 从本质上讲,可选定义如下所示:

 枚举可选 { 
无案
案一些(T)
}

意思是:

  //当您说 
让报价:字符串? = nil //您真正要说的是
let quote = Optional .none //类似
让报价:字符串? =“世界,您好!” //等同于
let quote = Optional .some(“你好,世界!”)

T代表通用类型,因此您可以创建任何类型的可选类型。

对象可以具有嵌套对象。 当您尝试访问嵌套对象之一而容器对象是可选对象时,您需要定义如何首先解开嵌套对象以访问嵌套对象。 我们已经将可选绑定视为解开可选对象的方法之一。 但是,当涉及到查询属性或在可选对象上调用方法时,每次绑定可选对象都是很麻烦的。 您想要的就是,如果可选选项不是nil ,则在其上调用某个方法或查询某个属性,这就是可选链接为您提供的功能。 您只需要在查询属性或调用方法时在点前添加问号。 这样,如果可选参数为nil ,则该语句将失败而不会崩溃您的应用程序。 否则,您的声明将成功运行。

  struct Person { 
变量名称:字符串
var car:汽车?
}结构车{
var座位数:整数
}让约翰=人(“约翰”)
let seatCapacity = john.car?.capacity // seatCapacity为零

到目前为止,可选选项似乎是解决缺乏价值的一种优雅方法,这是事实。 但是有时,当您知道可选对象不是nil时,每次都要解开可选对象就变得很乏味。 一个非常常见的情况是您的商店。 当尚未挂接到情节提要中的场景时初始化ViewController时,这些出口为nil 。 因此,您不能将它们定义为非可选变量,因为Swift会强制数据成员始终具有合法值。 但是,只要将场景与ViewController挂钩,便会设置每个出口,您可以放心地假设它们始终具有值。 Swift确实为您提供了该选项,名为“隐式解包的可选项”。 要定义一个,请在类型之后放置一个感叹号。 例如

 让backgroundView:UIView! 
backgroundView.backgroundColor = UIColor.blue

注意,在这里我没有使用可选的链接来访问backgroundColor 。 我不需要 因此,该名称隐式展开为optional 。 尽管很方便,但应谨慎使用。 访问一个不包含值的隐式展开的可选内容将导致运行时崩溃。 在为变量赋值访问变量并保证在容器对象的生命周期内保留此值时,它们很方便。 IBOutlets是典型示例,非常适合隐式展开的可选对象。

 因此,与其做 
@IBOutlet var nameLabel:UILabel?
@IBOutlet var nameLabel:UILabel!

有时,缺少值意味着立即退出代码块。 考虑一个示例,该示例基于可选的必需参数发出昂贵的网络请求。 如果将nil作为参数传递,将浪费服务器命中率。 结合guard声明,我们可以做到这一点:

 警卫让seachInput = searchTextField.text { 
//执行操作
}其他{
//提前退出
返回
}

在某些情况下, classstruct实例化可能会失败。 例如:

 让number = Int(“ Hello,World!”) 

在这里,我们试图从不是整数的字符串文字中初始化一个Int 。 在这种情况下,实例化将失败,并且将返回nil 。 例如:

  struct Person { 
初始化?(_字典:[String:Any]){
守卫让名字= dictionary [“ name”]为? 字符串else {return nil}
self.name =名称
}

通知init? 。 这就是使初始化程序失败的原因 。 如果初始化失败,则可失败的初始化程序必须返回nil ,因此从失败的初始化程序创建的实例始终是可选的

考虑以下情况:

 如果让profileImage = UIImage(named:“ profileImage”){ 
displayImage = profileImage
}其他{
displayImage = placeholderImage
}

本质上,我在这里所做的是提供备用或默认值 ,以防丢失主值。 这就是零促销运营商的工作方式。 例如

 让displayImage = UIImage(命名为“ profileImage”)吗?  placeholderImage 

这是一种在可选为nil提供默认值的便捷方法。 请注意,零促销业务并不能保证提供合法的价值。 如果默认值也恰好是可选的,则最终评估值也将是可选的。

尽管这篇文章不是关于错误处理的,但我将讨论可选选项在处理错误中的重要作用。 在Swift中, try有三种变体: trytry? try!

普通try有两个用例。 首先,您负责处理错误并提供catch块。 例如

 做{ 
让json =试试JSONSerialization.jsonObject(with:data,options:[])
} {
打印(error.localizedDescription)
}

第二种情况是将错误传递给呼叫链中的下一个呼叫者,例如

  func parseJson()引发{ 
让json =试试JSONSerialization.jsonObject(with:data,options:[])
}

现在是可选部分。 try! try? 。 您可以使用任何一个而不使用do-catch块。 您可能已经猜到了,请try! 将执行给定的语句,如果抛出错误,则程序立即终止。

 让json =试试!  JSONSerialization.jsonObject(with:data,options:[]) 

相反, try? 给您安全的双手,但提供了一个可选的选项。

 让json =试试吗?  JSONSerialization.jsonObject(with:data,options:[])// json是可选的(是吗?) 

在处理错误时,这为您提供了足够的灵活性。 您可以根据情况选择正确的try变体。

尽管可选选项在Swift中似乎是一个非常基本的功能,但是它的存在是有原因的,应该适当地使用它。 我已经看到开发人员忽略了此功能,并反复犯相同的错误,即在生产代码中访问nil 。 大量使用可选参数可使您的代码更具可读性和表现力。