您的位置:首页 > 运维架构

Optional小小总结

2017-04-12 13:32 246 查看
Optional概念


Swift中声明的一个变量时, 默认情况下它是non-optional的, 即必须赋予这个变量一个非空的值. 如果给non-optional类型的变量赋值nil, 编译器就会报错。Swift中的Optional作为一种类型,既可以存储一个值,也可以为空(就是swift里的nil);通常在定义变量或常量时,在类型后面加一个?表示它是Optional类型的的变量。

var number: Int? = 32

其实?只不过是一个语法糖,Optional的实际类型是一个enum:

enum Optional<T>: _Reflectable, NilLiteralConvertible {

    case None

    case Some(T)

    //...

}

上面的var number: Int? = 32也就可以表示为:

var numbet: Optional<Int> = 32,这也是原始语法,当然使用?更方便。

Optional的作用

Optional变量或常量只存在两种状态:包含一个值,或者为空;可以通过解包(unwrap)来获取值,为空的时候解包会异常。下面一段出自The Swift Programming Language (Swift 3)

The concept of optionals doesn’t exist in C or Objective-C. The nearest thing in Objective-C is the ability to return nil from a method that would otherwise return an object, with nil meaning “the absence of a valid object.” However, this only works for objects—it
doesn’t work for structures, basic C types, or enumeration values. For these types, Objective-C methods typically return a special value (such as NSNotFound) to indicate the absence of a value. This approach assumes that the method’s caller knows there is
a special value to test against and remembers to check for it. Swift’s optionals let you indicate the absence of a value for any type at all, without the need for special constants.

Swift通过引入Optional解决了Objective-C中“有”与“无”的问题,使代码的安全性得到了很大的提高,Swift是一种强类型语言。正如苹果所言, 可选类型就是证明Swift是一门安全的编程语言的一个小例子。
Swift的可选类型提供了在编译阶段就检查一些可能在运行时才会出现的常见错误的机制。

Optional使用场景

当一些属性值可以为空时,比如一个Person类中,name, address这类的属性都可以为空

当一个方法可以返回空值,比如类型转换函数:

//try to convert a String into an Int

let possibleNumber = "123"

let convertedNumber = Int(possibleNumber)

// convertedNumber is inferred to be of type "Int?", or "optional Int"

// 如果possibleNumber 是“hello”,则转换不会成功,就会返回nil

如果在一个字典中,使用key获取对应的value时,返回的也应该是Optional的值,因为你也可能找不到key对应的value,此时返回nil

一个方法可以返回一个值,如果方法内部产生了错误,也可以什么都不返回

Delegate 属性(不总是需要被赋值)

class中weak 类型的属性,他们所指向的值可以为空

一个大的资源可以随时被释放,以节约空间,所以正常情况下都是空的,只有使用时才会请求赋值。

Optional Binding可选绑定

出于类型安全的考虑,不能再把Optional当作Boolean值处理。像下面这条语句在swift中会遇到编译错误

var myString: String? = "Hello"

if myString {

    print(myString)

}

但是你可以通过==和!=,将Optional值和nil做比较来判断它是否包含一个值。如果不包含任何值,则为空。

if myString != nil{

    print("myString contain a string value of \(myString!)")

}

在上面的语句里,当我们确定myString包含一个值时,我们通过在myString后面添加一个!来进行强制解包(forced unwrapping),获取Optional内包含的值。

但是实际上,Swift提供了一种更加方便的形式来完成这一过程,所谓的Optional Binding,看下面的代码:

if let actualString = myString {

    print("myString contain a string value of \(actualString )")

} else {

    print("myString is nil")

}
上面代码的意思是,如果myString包含一个值,就将强制解包值赋给actualString,然后就可以在if语句里继续使用它了。我们自己不再需要对其进行解包了,这样我们就用Optinal binding 代替了强制解包(forced unwrapping)。我们也可以使用if var actualString = myString 来获取actualString,则这个actualString就是var类型的。if let`或者`if var`是可选绑定的两个关键字.
使用自然语言来描述上面这段代码的话, 意思就是如果myString有值,解包它,并且将它的值赋值给actualString, 然后执行下面的条件语句; 如果myString为空, 直接跳过条件语句块

隐式解包Optional

相较于普通的Optional,在Swift中我们还有一种特殊的Optional,在对它的成员或者方法进行访问时,程序员不再添加解包代码,被称为隐式解包Optional(Implicitly Unwrapped Optional)。在声明时,通过在类型后面添加!来告诉编译器这属于一个隐式解包Optional类型

let possisbleString: String!

隐式解包的Optional本质上与普通的Optional值并没有什么不同,只是在编译阶段,编译器会自动帮我们完成在变量后插入!的行为:

let possibleString: String! = "An implicity unwrapped optional string."

let implicitString: String = possibleString //此处我们不需要!来对possibleString 进行显示解包

很显然,隐式解包的写法会带来一个潜在的危险,如果尝试访问一个为空的隐式解包Optional, 就会遇到一个runtime error。

我们也可以像使用一个一般的Optional一样使用隐式解包Optional

比如判断是否为空:

if possibleString !=nil {

    //some action

}

比如使用if let 来进行optional binding:

if let implicitString = possibleString {

    print(implicitString)

}

当你知道变量可能为空的时候,不要使用隐式解包Optional。如果一个变量在它的声明周期里可能包含空,那你总是需要使用一般的Optional。

Optional Chaining

Optional Chaining,如同名字一样,我们可以通过一个链来安全的访问一个Optional的属性或者方法。

可以参看一个例子:

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {

    print("The star image is in PNG format")

}

这里通过在imagePath["star"]后面添加?来判断是否有值,如果存在则返回对应的path,然后紧接着调用path的hasSuffix方法。

使用Optional Chaining可以让我们摆脱很多不必要的判断和取值,从而精简代码。

??的使用

当Optional值为nil时,我们可以通过使用??来设置一个默认值。

let defaultImagePath = "/images/default.png"

let heartPath = imagePaths["heart"] ?? defaultImagePath

print(heartPath)

如果imagePaths中没有包含"heart"对应的value,则会把defaultImagePath赋给heartPath,也就会打印出/images/default.png

当然了,我们也可以将??链接起来,设置多重默认值:

let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath

这样每一次值为nil时都会设置默认的值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: