Why Optional#
引入optional可以解决两个问题,
- 解决潜在crash问题
[dict setObject: objc forKey: @“someKey"];
比如OC中像集合dict的someKey设置值为objc, 当objc为nil时,程序运行到这句代码时会crash,利用 optional 的机制更严格的类型检查,使得在编译时可以发现更多问题,把这种潜在的错误及时的排除掉了;
- 解决空类型不详的问题,“无”和“空”划分得更清晰,表述更明确, 让代码可读性更高。
定义#
OC和Swift中通过不同的修饰符来表示optional变量,由于swift有语法糖的支持,所以OC相对于Swift来说语法上略显得啰嗦了一些。但是在OC中为了防止写一大堆 nonnull,Foundation 还提供了一对宏,包在里面的对象默认加 nonnull 修饰符,只需要把 nullable 的指出来就行。
#import "Ship.h"
NS_ASSUME_NONNULL_BEGIN
@interface ShipList : NSObject
- (nullable Ship*)shipWithName: (NSString *)name;
- (NSInteger)indexOfShip: (Ship *)ship;
@property (copy, nullable) NSString *name;
@property (copy, readonly) NSArray *allShips;
@end
NS_ASSUME_NONNULL_END
其实OC中并没有真正支持optional, 只是编译器帮忙做了检查,会对错误提出警告,但是程序依然可以通过编译,在运行时会crash。
而相同的代码用Swift实现,编译器在编译过程中就会报错。
Swift中的Optional是由ENUM + 范型定义的一个数据类型; Optional.none 是空的字面量,Optional.some(Wrapped) 存储了一个包裹的值。引入Optional封装nil,nil有类型后,才能融入强类型语言范畴。
这个被包裹的值就好像礼物盒子里的礼物一样,你只有把盒子打开才知道里面的东西到底是什么。
struct Gift {
let sender: String = "June"
let name: String = "💄"
}
func whatIsTheGift(giftBox: Gift?) {
if let gift = giftBox {
print("the giftis \(gift.name)")
} else {
print("there is no gift")
}
}
var 🎁: Gift?
whatIsTheGift(🎁)
// there is no gift
🎁 = Gift()
whatIsTheGift(🎁)
// the gift is 💄
而打开礼物这个过程我们通常称为unwrapp(解包或者拆包),Swift有以下几种方式进行解包:
* ! (直接解包,快速,短小精悍, 但如果沒值会直接crash,使用的时候一定要保证optional变量有值)
* if / switch (可以避免沒有值还硬拆的狀況,也可以处理沒有值的情况)
* if let / guard let (可以从第三方拆完直接拿到值,不用自已拆)
* ?? (空合运算符,当optional变量为 nil 时,直接提供符合逻辑的初始值,但是可能引起难以定位到错误原因的问题,慎用)
举个🌰,一个optional
变量 name
,当值为June
时,5种方式进行解包都可以,打印结果一致。
但是当
optional
变量 name
值为nil
时, 直接解包的方式会crash掉。而空合运算符解包的方式打印结果与其他三种并不相同。所以总体来说应该首选if let / guard let
, 最不建议直接使用!
,而??
根据使用场景来选择,慎用。