一、哨岗值
int ch;
while ((ch = getchar()) != EOF) {
printf("Read character %c\n", ch);
}
printf("Reached end-of-file\n");
像上面的EOF,这种东西被叫做哨岗值,其广泛存在与c、C++、java之中。其存在有其合理性,但是却很不合理,其混淆了正常值、需要一些潜规则知识等等,不合理因素。
Tony Hoare 在 1965 年设计了 null 引用,他对此设计表示痛心疾首,并将这个问题称为 “价值 十亿美元的错误”。
二、通过枚举解决魔法数的问题
上文中的哨岗值其实就是一个魔法数,大家都知道这是不好的。所以在Swift中,引入了可选值(通过枚举、关联值、泛型来实现)。
// 可选值的核心代码
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
// 由于可选值遵循了ExpressibleByNilLiteral协议,所以可是用nil来代替none
三、可选值概览
var array = ["one", "two", "three", "four"]
// 检查可选值是否为 nil,如果不是 nil,便会解包可选值
// idx 的类型就是 Int (而不再是可 选值),并且 idx 也只在这个 if let 语句的作用域中有效
if let idx = array.firstIndex(of: "four") {
array.remove(at: idx)
}
// while let 语句和 if let 非常相似,它表示当一个条件返回 nil 时便终止循环。
while let line = readLine() {
print(line)
}
// 为了解决if、while、switch解包后值的作用域问题,引入了guard语句
// 可选链
// ?? 运算符
// 在字符串插值中使用可选值
let bodyTemperature: Double? = 37.0
let bloodGlucose: Double? = nil
print(bodyTemperature) // Optional(37.0)
// 警告:表达式被隐式强制从 'Double?' 转换为 Any
print("Blood glucose level: \(bloodGlucose)") // Blood glucose level: nil // 警告:字符串插值将使用调试时的可选值描述,
// 请确认这确实是你想要做的。
infix operator ???: NilCoalescingPrecedence
public func ???<T>(optional: T?, defaultValue: @autoclosure () -> String) -> String
{
switch optional {
case let value?: return String(describing: value)
case nil: return defaultValue()
}
}
print("Body temperature: \(bodyTemperature ??? "n/a")") // Body temperature: 37.0
print("Blood glucose level: \(bloodGlucose ??? "n/a")")
// Blood glucose level: n/a
可选值提供了map、flatMap、compactMap方法。
四、强制解包的时机
当你能确定你的某个值不可能是 nil 时可以使用叹号,你应当会希望如果它意外是 nil 的话,程序应当直接挂掉。
// 提供!!运算符,用于强制解包时,能够输出友好的信息
infix operator !!
func !! <T>(wrapped: T?, failureText: @autoclosure () -> String) -> T { ifletx=wrapped{returnx}
fatalError(failureText())
}
let s = "foo"
let i = Int(s) !! "Expecting integer, got \"\(s)\""
五、隐式解包可选值
强制解包会引发崩溃,但是隐式解包与强制解包类似,但是它依然存在。
- 在使用从nib获取的outlet时,此时一般使用隐式解包
- 为了与OC代码兼容