美文网首页
Swift基础之Optional

Swift基础之Optional

作者: SimonMont | 来源:发表于2019-09-18 14:57 被阅读0次
    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

          本文链接:https://www.haomeiwen.com/subject/qpfjyctx.html