import UIKit
import PlaygroundSupport
/// 可选值: Optional
/// Mark: 可选值的由来
/// 在编程语言中有一些特定的值来代表特定的含义:比如:EOF(对-1 的#define)代表文件的结束,null代表空
///这些特定的值用来表示函数没有返回真实的值。这些值被称为“岗哨值”
///岗哨值是有缺陷的
/// 1、这个特定值未被定义,只能通过文档查看, 2、很容易引起其他问题。例如下面代码
//NSString * someString = "hello"
//if ([someString rangeOfString:@"swift"].location != NSNotFound) {
// // 如果someString为空,那么location为0, 而NSNotFound 被定义为NSIntegerMax,这样if内语句将被执行
// print("xxxxx")
//}
//3、如果一个函数的返回值一定不为空,那么只能通过文档才能知道,从函数本身是看不出来的
// Mark: 通过可选值解决上述问题
//使用swift枚举中含有“关联值”特性(有些语言中被称为标签联合或可辨识联合)
//将若干种不同的可能类型保存在内存的同一空间中,并使用一个标签来区分到底被持有的是什么类型
//enum Optional<Wrapped> {
// case none
// case some(Wrapped)
//}
///获取关联值的唯一方法: 通过switch 或者 if case 进行模式匹配。除非显式进行捷豹,否则不可能意外使用Optional中的值
///因为可选值在swift中非常基础,所以有更简单的写法:Optional<Index> 可以写作: Index?;
///可选值遵循 ExpressibleByNilLiteral协议,可以用nil代替.none;
///非可选值在需要时可自动升级为可选值: return .some(index) 可以写作:return index
///可选值的本质就是普通的枚举值
var array = ["one", "two", "three"]
switch array.firstIndex(of: "four") {
case .some(let idx):
array.remove(at: idx)
default:
break
}
//简化写法
switch array.firstIndex(of: "four") {
case let idx?: //?作为匹配模式后缀
array.remove(at: idx)
case nil:
break //nil 匹配.none
}
// Mark: if let 可选绑定
if let idx = array.firstIndex(of: "two") {
array.remove(at: idx)
}
// if 可以绑定多个值,而且后面绑定的值可以基于之前解包成功的值进行操作
let urlString = "https://www.objc.io/logo.png"
if let url = URL(string: urlString),
let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
let view = UIImageView(image: image)
PlaygroundPage.current.liveView = view
}
//if 可以有用Bool值限定和可选绑定混用
if let url = URL(string: urlString),
url.pathExtension == "png",
let data = try? Data(contentsOf: url),
let image = UIImage(data: data) {
let view = UIImageView(image: image)
PlaygroundPage.current.liveView = view
}
// Mark: while let 可选值绑定
//while let line = readLine(), !line.isEmpty {
// print(line)
//}
// Mark: while 配合Iterater使用: for x in seq 要求 seq遵循Sequence协议,该协议提供makeIterator方法创建迭代器,迭代器next方法负责返回序列中的值,当序列值耗尽时,返回nil
let numers = [1, 2, 3]
var iterator = numers.makeIterator()
while let i = iterator.next() {
print(i, terminator:" ")
}
for i in 0..<10 where i % 2 == 0 {
print(i, terminator: " ")
}
var iterator2 = numers.makeIterator()
while let i = iterator2.next() {
if i % 2 == 0 {
print(i, terminator: " ")
}
}
// while 和 for是有区别的 : for 等价于 while let ,而while就是while
var functions:[() ->Int] = []
for i in 0..<3 {
functions.append({i}) //每次迭代都是不同的变量,虽然名字一样,但是已经不是原来的变量,相当于 let i1, let i2, let i3(解释上面 for 等价于 while let)
}
print("***")
for f in functions {
print("\(f())", terminator: "") // 在其他语言中:因为闭包会捕获局部变量,虽然每次存储闭包时,i的值不一样,但是所有的闭包捕获的是同一个局部变量,所以当闭包执行时,打印的都是2(i的最后一个值)
// swift当中每次append的时候都是不同的i变量
}
// Mark: 双重可选值: 可选值被另外一个可选值包装
let stringNumbers = ["1", "2", "three"]
let maybeInts = stringNumbers.map{ Int($0) } // Optional(1), Optinal(2), nil //maybeInts是一个类型为Optional<Int>的数组,因为Int.init(String)可能失败
//mabeInts中最后一个元素是nili
for maybeInt in maybeInts {
//得到两个Int? 和 一个nil
print(maybeInt, terminator: " ")
}
print()
//iterator3.next() 返回的是Optonal<Optonal<Int>> 或者说是 Int??
var iterator3 = maybeInts.makeIterator()
while let maybeInt = iterator3.next() { //while let 会进行第一层解包(最外层),所以最后一个值是.some(nil)
print(maybeInt, terminator: " ")
}
print()
//for写法:使用case来进行模式匹配
// 方式 1
for case let i? in maybeInts { // i? 是 .some(i)的简写
print(i, terminator: " ")
}
print()
// 方式 2
for case .some(let i) in maybeInts {
print(i, terminator: " ")
}
print()
// 方式 3
for case let .some(i) in maybeInts {
print(i, terminator: " ")
}
//for let x in maybeInts { //不存在这种写法: 因为 for x in maybeInts 就已经表达了这个意思, 不过可以用 for var x in mybeInts
// print(x)
//}
// Mark: 基于case模式匹配的规则应用到for,if,while中
let j = 5
if case 0..<10 = j { // 0 到9 任意一个值等于j
print("\(j)在范围内")
}
//Mark: 重载case模式匹配
//因为case匹配可以通过重载~=运算符来进行扩展,所以可以将if case 和 for case 进行有趣的扩展
struct Pattern {
let s: String
init(_ s: String) {
self.s = s
}
}
func ~=(pattern: Pattern, value: String) -> Bool {
return value.range(of: pattern.s) != nil
}
let s = "Simon Swift"
if case Pattern("Swfit") = s {
print("\(String(reflecting: s)) contains \"Swift\"")
}
//恶作剧代码
//func ~=<T, U>(_: T,_: U) -> Bool { return true}
// 除了 let意外, var也可以搭配 for , while, if 等使用。用法同 let一样
// Mark: 解包后的作用域
// if语句中的解包变量只能在if作用域中使用,不能再外面使用
if let firstElement = array.first {
print(firstElement)
}
//外面无法使用firstElement
//swift延时初始化能力
extension String {
var fileExtension: String? {
let period: String.Index //定义局部变量且未初始化
if let idx = lastIndex(of: ".") {
period = idx //初始化
} else {
return nil
}
let extStart = index(after: period)
return String(self[extStart...])
}
}
// Mark: guard。 等价于 if not let
//guard let x = ... else { ...} . guard的唯一要求是: 在else中必须离开作用域,既return, 或 在循环中使用break,continue
///一个函数的返回值如果是Never的话,这就意味着告诉编译器这个函数不会返回。通常有两种函数会这样做: 一类是 fatalError,另一个是dispatchMain那样在整个生命周期的函数。
/// 编译器会使用这个信息进行控制流诊断。比如guard语句的else语句路径必须退出当前域或者调用一个不会反悔的函数
///Never 又被叫做无人类型。这种类型没有有效值,因此也不能够被构建。它的唯一作用就是给编译器提供信号。一个返回值被声明为无人类型的函数将永远不能正常返回。
///在swift中,无人类型由一个不包含任意成员的enum来实现的
public enum Never{}
// Mark: Never用法
//关闭编译器提示错误
func unimplemented() -> Never {
fatalError("编译器先闭嘴,我还没写完呢,不要给我提示错误")
}
/// Mark: Swift 各种“无”类型: nil, Never,Void。 Void标识空元组
public typealias Void = ()
///Swift对“东西不存在”(nil),“存在且为空”(Void) i以及 “不可能发生”(Never)定义的非常清楚
// Mark: 可选链: swift中可以通过“可选链” 实现“对nil发送消息什么都不会发生”
// delegate?.callBack 。 swift会强制要求你声明的消息的接受者可能为nil
let str: String? = "Never say never"
// 为什么在第一个uppercase()后加问号,而后面的不需要? 因为可选链是一个“展平”操作,str?.uppercase()返回一个可选值,如果再调用?.lowercased()将返回一个可选值的可选值。而我们要的是一个可选值,所以后面的不用加,可选特性已经在前面捕获了
let result = str?.uppercased().lowercased()
//如果返回值是可选值的话,那么后面仍然需要添加问号来表示正在链接这个可选值
extension Int {
var half: Int? {
guard self < -1 || self > 1 else { return nil }
return self / 2
}
}
20.half?.half?.half //Optional(2) 编译器会展平结果类型,结果是Int? 而不是Int???
let dictOfArray = ["nine": [0, 2, 3, 4]]
dictOfArray["nine"]?[3]
let dictOfFunctions: [String:(Int, Int) -> Int] = [
"add":(+),
"substract": (-)
]
dictOfFunctions["add"]?(1,2)
// Mark: 通过可选链进行赋值
struct Person {
var name: String
var age: Int
}
var optionalLisa: Person? = Person(name: "Lisa Simpson", age: 8)
if optionalLisa != nil {
optionalLisa!.age += 1
}
if var lisa = optionalLisa {
lisa.age += 1 //此时optionalLisa的值不会改变,因为Person是值类型,可选绑定会创建一个副本,即使lisa的值改变,也不会影响optionalLisa的值 。如果Person是类的话,是可以的
}
//使用可选值赋值
optionalLisa?.age += 1
// Mark: 可选值可以直接赋值
var a: Int? = 5
a? = 10
// Mark: nil合并运算符: ??
//在解包一个可选值时,如果是nil,可以用一个默认值来替换它
let stringteger = "1"
let number = Int(stringteger) ?? 0
// lhs ?? rhs 等价于 lhs!=nil? lhs:rhs
let array1 = [1, 2, 3]
!array.isEmpty ? array1[0] : 0
// 可以通过 ?? 完成合并操作
array1.first ?? 0
//使用可选值确保满足某个条件
array1.count > 5 ? array1[5] : 0
extension Array {
subscript(guarded idx: Int) -> Element? {
guard (startIndex..<endIndex).contains(idx) else { return nil }
return self[idx]
}
}
array1[guarded: 5] ?? 0
//多个可选值合并
let i: Int? = nil
let l: Int? = nil
let k: Int? = 45
i ?? l ?? k ?? 0 // 42
// Mark:字符串插值使用可选值
let bodyTemperature: Double? = 37.0
let bloodGlucose: Double? = nil
print(bodyTemperature) //警告:表达式隐式强制从 “Double?”转换为Any
print("Blood glucose leve:\(bloodGlucose)") //警告: 字符串榨汁将使用调试时的可选值描述
// ?? 表达式要求两侧的类型必须匹配: Int?的默认值必须是Int, Double?的默认值必须是Double
// Mark: 自定义运算符
infix operator ???: NilCoalescingPrecedence
public func ???<T>(optional: T?, defaltValue: @autoclosure() -> String) -> String { //@autoclosure()标注确保只有当需要时,才对第二个表达式求值
switch optional {
case let value?:
return String(describing: value)
case nil:
return defaltValue()
}
}
print("Body temperature:\(bodyTemperature ??? "a/n")")
// Mark: 可选值map
//可选值拥有map功能,当有值时则调用闭包。 区别于结合中的map,可选值map只操作一个值
let characters:[Character] = ["a", "b", "c"]
let firstChar = characters.first.map{String($0)}
// Mark: 可选值map原理
extension Optional {
func map<U>(transform:(Wrapped) -> U) -> U? {
if let value = self {
return transform(value)
}
return nil
}
}
// 实现不接受初始值的reduce方法
extension Array {
func reduce(_ nextPartialReult:(Element, Element) ->Element) -> Element? {
guard let fst = first else { return nil }
return dropFirst().reduce(fst, nextPartialReult)
}
}
// 不使用gaurd实现reduce
extension Array {
func reduce_alt(_ nextPartialResult: (Element, Element) -> Element) -> Element? {
return first.map {
dropFirst().reduce($0, nextPartialResult)
}
}
}
// Mark: 可选值flatMap : 将多层可选值展平
let stringNumbers2 = ["1", "2", "3", "foo"]
let x = stringNumbers2.first.map{Int($0)} //Optional(Optional(1)) // first 返回值是可选值,Int(String)返回值也是可选值,所以x 为Int??
//flatMap把结果展平为Int?
let y = stringNumbers2.first.flatMap{Int($0)} //Optional(1)
//flatMap 等价于 if let
extension Optional {
func flatMap<U>(transform:(Wrapped) -> U?) -> U? {
if let value = self, let transformed = transform(value) {
return transformed
}
return nil
}
}
//Mark: 使用可选值过滤 nil
let numbers1 = ["1", "2", "3", "foo"]
var sum1 = 0
for case .some(let i) in numbers1.map({Int($0)}) {
sum1 += i
}
print(sum1)
let sum2 = numbers1.map({Int($0)}).reduce(0){$0 + ($1 ?? 0)}
print(sum2)
//因为现在还无法定义一个只作用于可选值序列的Sequence扩展,所以定义为全局函数
func flatten<S: Sequence, T>(source: S) -> [T] where S.Element == T? {
let filtered = source.lazy.filter {$0 != nil}
return filtered.map{$0!}
}
extension Sequence {
func flatMap<U>(transform:(Element) -> U?) -> [U] {
return flatten(source: self.lazy.map(transform))
}
}
// Mark: 可选值判等
//可选值判等重载==运算符
func ==<T: Equatable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case (nil, nil):
return true
case let (x?, y?):
return x == y
case (nil,_?),(_?,nil):
return false
}
}
let regex = "^Hello"
if regex.first == Optional("^") {
//如果使用非可选值和可选值匹配时,swfit会将非可选值”升级“为可选值,所以这段代码不可取。理想状态时:regex.fist == "^",这种写法比较简单
}
//推导: 为了实现非可选值和可选值判等的简单写法,我们需要以下三个函数
// func ==<T: Equatable>(lhs: T?, rhs: T?) -> Bool
// func ==<T: Equatable>(lhs: T, rhs: T?) -> Bool
// func ==<T: Equatable>(lhs: T?, rhs: T) -> Bool
//事实上只需要一种,因为Swift包含隐士转换,比如map函数返回值是可选的,但是我们不用写成这样: return Optional(1), 而是直接写 return 1
// Mark: 注意"字典"赋值nil时操作
var dictWithNils: [String: Int?] = [
"one" : 1,
"two" : 2,
"three" : 3,
"foure" : 4,
"none" : nil
]
dictWithNils["one"] = nil //会移除键
print(dictWithNils)
dictWithNils["two"] = Optional(nil) //会移除键
print(dictWithNils)
dictWithNils["three"] = .some(nil) //不会移除键
print(dictWithNils)
dictWithNils["foure"]? = nil //不会移除键 //因为是用的可选链,如果键不存在,后面赋值不会执行,也不会插入
print(dictWithNils)
dictWithNils["five"] = Optional(5)
print(dictWithNils)
// Mark: Equalable 和 ==
//Equalble是协议, == 是操作符
// Mark: 强制解包
//当结果不可能为nil时使用强制解包
func flatten2<S: Sequence, T>(source: S) -> [T] where S.Element == T? {
let filtered = source.lazy.filter {$0 != nil}
return filtered.map{$0!} //因为上一句已经保证了每个元素不可能为nil
}
// Mark: 改进强制解包错误信息: 定义!!操作符
infix operator !!
func !!<T>(Wrapped: T?, failureText: @autoclosure() -> String) -> T {
if let x = Wrapped { return x }
fatalError(failureText())
}
//let ss = "foo"
//let yyy = Int(ss) !! "失败鸟"
// Mark: 在调试版本中进行断言
//一般在发布版本中会把”解包失败“替换成空值或默认值,而在测试中一般通过断言,让程序崩溃
// 实现疑问感叹号(!?)操作符,对解包失败进行断言,切在发布版本总替换成默认值
infix operator !?
func !?<T: ExpressibleByIntegerLiteral>(wrapped: T?, failureText: @autoclosure() -> String) -> T {
assert(wrapped != nil, failureText())
return wrapped ?? 0
}
let sss = "20"
let iii = Int(sss) !? "Expecting integer,got\(s)"
// 对字面量转换协议进行重载,可以覆盖不少能够有默认值的类型
func !?<T: ExpressibleByArrayLiteral> (wrapped: T?, failureText: @autoclosure() -> String) -> T {
assert(wrapped != nil, failureText())
return wrapped ?? []
}
func !?<T: ExpressibleByStringLiteral>(wrapped: T?, failureText: @autoclosure() -> String) -> T {
assert(wrapped != nil, failureText())
return wrapped ?? ""
}
// Mark: 隐式解包
//隐式解包的可选值看上去像非可选值,但本质还是可选值。你可以像可选值一样使用它
var ssss: String! = "Hello"
ssss?.isEmpty
if let x = ssss {print(x)}
Swift基础之Optional
©著作权归作者所有,转载或内容合作请联系作者
- 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
- 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
- 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
推荐阅读更多精彩内容
- Optional 可选值 Optional 是 Swift 的一大特色,也是 Swift 初学者最容易困惑的问题 ...
- 可选值: Optional有两种状态:1.有值 2.没有值, 没有值就是nil 可选值可以利用if语句来进行判断 ...