美文网首页
Swift语法知识汇总(下)

Swift语法知识汇总(下)

作者: Amok校长 | 来源:发表于2020-06-17 15:34 被阅读0次

    第十二章 Error处理、泛型

    1-1、错误类型

    /*开发过程常见的错误:
        语法错误(编译报错)
        逻辑错误
        运行时错误(可能会导致闪退, 一般也叫做异常)
    */
    

    1-2、自定义错误

    /*Swift中可以通过Error协议自定义运行时的错误信息.
        枚举、类、结构体都可以定义Error.
    */
    ///方案一: 普通处理
    func divide(_ num1: Int, _ num2: Int) -> Int {
      if num2 == 0 {
        return nil
      }
      return num1 / num2
    }
    ///方案二: 用自定义Error及使用
    struct MyError : Error {
      var msg: String
    }
    
    func divide(_ num1: Int, _ num2: Int) throws -> Int {//throws表示这个函数有可能会抛出错误
      if num2 == 0 {
        throw MyError(msg: "0不能作为除数")
      }
      return num1 / num2
    }
    divide(20, 0)//报错
    
    ///方案三: 自定义Error处理
    enum SomeError : Error {
      case illegalArg(String)
      case outOfBounds(Int, Int)
      case outOfMemory
    }
    //函数内部通过throw抛出自定义Error, 可能会抛出Error的函数必须加上throws声明
    func divide(_ num1: Int, _ num2: Int) throws -> Int {
      if num2 == 0 {
        throw SomeError.illegalArg("0不能作为除数")
      }
      return num1 / num2
    }
    //需要使用try调用可能会抛出Error的函数
    var result = try divide(1, 0)
    print(result)
    
    /* do-catch */
    /// 可以使用do-catch捕捉Error
    func test() {
      print("1")
      do { //异常的代码发到do里面去
        print("2")
        print(try divide(20, 0))///一旦抛出Error后, try下一句直到作用域结束的代码都将停止运行
        print("3")
      } catch let SomeError.illegalArg(msg) {
        print("参数异常:", msg)
      } catch let SomeError.outOfBounds(size, index) {
        print("下标越界:","size=\(size)","index=\(index)")
      } catch SomeError.outOfMemory {
        print("内存溢出")
      } catch {//catch写捕获异常的代码
        print("其他错误")
      }
      print("4")
    }
    
    test()
    // 1
    // 2
    // 参数异常: 0 不能作为除数
    // 4
    

    1-3、处理Error

    /*处理Error的2种方式
        ① 通过do-catch捕捉Error
        ② 不捕捉Error, 在当前函数增加throws声明, Error将自动抛给上层函数
    如果最顶层函数(main函数)依然没有捕捉Error, 那么程序将终止
    */
    func test() throws {
      print("1")
      print(try divide(20, 0))
      print("2")
    }
    try test()
    // 1
    // Fatal error: Error raised at top level
    
    do {
      print(try divide(20, 0))
    } catch is SomeError {//判断如果是SomeError类型, 就执行这里
      print("SomeError")
    }
    
    do {
      print(try divide(20, 0))
    } catch let error {
      switch error {
      case let SomeError.illegalArg(msg):
        print("参数错误: ", msg)
      default: 
        print("其他错误")
      }
    }
    
    // ②
    func test() throws {
      print("1")
      do {
        print("2")
        print(try divide(20, 0))
        print("3")
      } catch let error as SomeError {
        print(error)
      }
      print("4")
    }
    try test()
    // 1
    // 2
    // illegalArg("0不能作为除数")
    // 4
    

    1-4、try?、try!

    /*可以使用try?、try!调用可能会抛出Error的函数, 这样就不用处理Error*/
    func test() {
      print("1")
      var result1 = try? divide(20, 10) // Optional(2), Int?
      var result2 = try? divide(20, 0) // nil
      var result3 = try! divide(20, 10) // 2, Int
      print("2")
    }
    test() 
    
    /*a、b是等价的*/
    var a = try? divide(20, 0)
    var b: Int?
    do {
      b =  try divide(20, 0)
    } catch {  b = nil }
    

    1-5、rethrows

    /*rethrows表明: 函数本身不会抛出错误, 但调用闭包参数抛出错误, 那么它会将错误向上抛*/
    func exec(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
      print(try fn(num1, num2))
    }
    // Fatal error: Error raised at top level
    try exec(divide, 20, 0)
    
    // throws和rethrows效果一样, 都是抛出异常; 不同点是throws是其函数调用参数导致的异常, rethrows是闭包参数导致的异常
    

    1-6、defer

    /*defer语句: 用来定义以任何方式(抛错误、return等) 离开代码块前必须要执行的代码
        defer语句将延迟至当前作用域结束之前执行
    */
    func divide(_ num1: Int, _ num2: Int) throws -> Int {
      if num2 == 0 {
        throw SomeError.illegalArg("0不能作为除数")
      }
      return num1 / num2
    }
    func open(_ filename: String) -> Int {
      print("open")
      return 0
    }
    func close(_ file: Int) {
      print("close")
    }
    func processFile(_ filename: String) throws {
      let file = open(filename)
      defer {
        close(file)
      }
      // 使用file
      // ...
      try divide(20, 0)
      
      //close 将会在这里调用
    }
    try processFile("test.txt")
    // open
    // close
    // Fatal error: Error raised at top level
    
    /*defer语句的执行顺序与定义顺序相反*/
    func fn1() { print("fn1") }
    func fn2() { print("fn2") }
    func test() {
      defer { fn1() }
      defer { fn2() }
    }
    test()
    // fn2
    // fn1
    

    2-1、泛型(Generics)

    /*泛型可以将类型参数化, 提高代码复用率, 减少代码量*/
    func swapValues<T>(_ a: inout T, _ b: inout T) {//T代表一种不确定的类型
      (a, b) = (b, a)
    }
    var n1 = 10
    var n2 = 20
    swapValues(&n1, &n2)
    
    var d1 = 10.0
    var d2 = 20.0
    swapValues(&d1, &d2)
    
    struct Date {
      var year = 0, month = 0, day = 0
    }
    var dd1 = Date(year: 2011, month: 9, day: 10)
    var dd2 = Date(year: 2012, month: 10, day: 11)
    swapValues(&dd1, &dd2)
    
    /*泛型函数赋值给变量*/
    var fn: (inout Int, inout Int) -> () = swapValues
    
    func test<T1, T2>(_ t1: T1, _ t2: T2) {}
    var fn: (Int, Double) -> () = test
    
    class Stack<E> { //泛型类
      var elements = [E]()
      func push(_ elements: E) { elements.append(element) }
      func pop() -> E { elements.removeLast() }//删除数组最后一个元素
      func top() -> E { elements.last! }//查看数组最后一个元素
      func size() -> Int { elements.count }
    }
    var intStack = Stack<Int>()
    intStack.push(11)
    intStack.push(22)
    intStack.push(33)
    print(intStack.top()) //33
    print(intStack.pop()) //33
    print(intStack.pop()) //22
    print(intStack.pop()) //11
    print(intStack.size()) //0
    var anyStack = Stack<Any>()
    
    class SubStack<E> : Stack<E> {}
    
    struct Stack<E> {
      var elements = [E]()
      mutating func push(_ element: E) { elements.append(element) }
      mutating func pop() -> E { elements.removeLast() }//在结构体中修改结构体中属性的内存, 要加mutating
      func top() -> E { elements.last! }
      func size() -> Int { elements.count }
    }
    
    enum Score<T> {
      case point(T)
      case grade(String)
    }
    let score0 = Score<Int>.point(100)
    let score1 = Score.point(99)
    let score2 = Score.point(99.5)
    let score3 = Score<Int>.grade("A")//就算没有用到point(), 也要写上T的类型
    

    3-1、关联类型(Associated Type)

    /*关联类型的作用: 给协议中用到的类型定义一个占位名称
        协议中可以拥有多个关联类型
    */
    protocol Stackable {//协议想实现泛型的话, 就用关联类型.
        associatedtype Element //关联类型
      associatedtype Element2 //关联类型
      mutating func push(_ element: Element)
      mutating func pop() -> Element
      func top() -> Element
      func size() -> Int
    }
    
    class StringStack : Stackable {
        // 给关联类型设定真实类型
      // typelias Element = String //方法一
      var elements = [String]()
      func push(_ element: String) { elements.append(element) }
      func pop() -> String { elements.removeLast() }
      func top() -> String { elements.last! }
      func size() -> Int { elements.count }
    }
    var ss = StringStack()
    ss.push("Jack")
    ss.push("Rose")
    
    class Stack<E> : Stackable {
      // typealias Element = E
      var elements = [E]()
      func push(_ element: E) {
        elements.append(element)
      }
      func pop() -> E { elements.removeLast() }
      func top() -> E { elements.last! }
      func size() -> Int { elements.count }
    }
    

    3-2、类型约束

    protocol Runnable { }
    class Person { }
    func swapValues<T : Person & Runnable>(_ a: inout T, _ b: inout T) {
      (a, b) = (b, a)
    }
    
    protocol Stackable {
      associatedtype Element: Equatable//这里指定了关联类型遵守Equatable协议(系统自带的:Equatable可等价)
    }
    class Stack<E : Equatable> : Stackable { typelias Element = E }//因为协议使用了关联类型, 此处必须指明关联类型是什么
    
    func equal<S1: Stackable, S2: Stackable>(_ s1: S1,_ s2: S2)->Bool where S1.Element == S2.Element, S1.Element : Hashable {//指定了泛型要遵守Stackable协议,where条件约束: S1的关联类型Element跟S2的关联类型Element是相等的, 指定S1关联类型Element遵守Hashable协议(系统自带的:Hashable可哈希)
      return false
    }
    
    var s1 = Stack<Int>()
    var s2 = Stack<Int>()
    var s3 = Stack<String>()
    equal(s1, s2)
    equal(s1, s3) // error: requires the types 'Int' and 'String' be equivalent
    

    3-3、协议类型的注意点

    protocol Runnable {}
    class Person : Runnable {}
    class Car: Runnable {}
    
    func get(_ type: Int) -> Runnable {
      if type == 0 {
        return Person()
      }
      return Car()
    }
    var r1 = get(0)
    var r2 = get(1)
    
    /*如果协议中有associatedtype*/
    protocol Runnable {
      assciatedtype Seed
      var speed: Speed { get }
    }
    class Person : Runnable {
        var speed: Double { 0.0 }
    }
    class Car : Runnable {
      var speed: Int { 0 }
    }
    
    func get(_ type: Int) -> Runnable {//报错: 因为无法编译时期确定speed类型
      if type == 0 {
        return Person()
      }
      return Car()
    }
    var r1 = get(0)
    var r2 = get(1)
    
    /*泛型解决*/
    // 解决方法①: 使用泛型
    func get<T: Runnable>(_ type: Int) -> T {//返回值类型是泛型, 且遵守Runnable协议
      if type == 0 {
        return Person() as! T
      }
      return Car() as! T
    }
    var r1: Person = get(0)
    var r2: Car = get(1)
    
    // 解决方案②: 使用some关键字声明一个不透明类型(Opaque Type),目的:不公开内部细节,只开发部分接口
    func get(_ type: Int) -> some Runnable {//带有Runnable的协议作为返回类型的话.some设定: 要求只能返回一种, 不能返回两种类型.
    //  if type == 0 {
    //    return Person()
    //  }
      return Car()
    }
    var r1 = get(0)
    var r2 = get(1)
    
    /// some除了用在返回值类型上, 一般还可以用在属性类型上
    protocol Runnable { associatedtype Speed }
    class Dog : Runnable { typealias Speed = Double }
    class Person {
      var pet: some Runnable {// 养了一只宠物,又不想让人知道养的什么宠物.只想让人知道它是遵守Runnable协议的
        return Dog()
      }
    }
    

    第十三章 汇编分析String、Array底层

    1-1、关于String的思考

    /*1个String变量占用多少内存?
        下面2个String变量, 底层存储有什么不同?
    */
    var str1 = "0123456789"//全局变量 16个字节
    var str2 = "0123456789ABCDEF"
    
    /*内存地址从低到高:
    代码区->常量区->全局区 (数据段)->堆空间->栈空间->动态库
    */
    
    /*如果对String进行拼接操作, String变量的存储会发生什么变化?*/
    str1.append("ABCDE")
    str1.append("F")
    
    str2.append("G")
    
    /*ASCII码表: https://www.ascii-code.com/ */
    
    var str1 = "0123456789"//如果字符串长度小于等于F,会直接将字符串数据存在字符串内存里面
    // 0x3736353433323130 0xea00000000003938
    // 0xea   里面的a表示长度,此时是10位,所以是a; Oxe表示类型标识
    // 类似于OC的tagger pointer
    print(Mems.memStr(ofVal: &str1))
    
    // var str1 = "0123456789ABCDE"
    // 0x3736353433323130 0xef45444342413938
    // print(Mems.memStr(ofVal: &str1))
    
    var str2 = "0123456789ABCDEF"//调用str2的初始化器
    // 0xd0000000000010 0x800000010000a790 //0x800000010000a790(后一段)这8个字节中决定了字符串的真实地址值,实际内容放在地址值指向的内存里; 0xd0000000000010(前一段)中的10代表字符串的长度,0xd0是标志位.
    // 字符串的真实地址 + 0x7fffffffffffffe0 = 0x800000010000a790
    // 字符串的真实地址 = 0x800000010000a790 - 0x7fffffffffffffe0 
    // 字符串的真实地址 = 0x000000010000a790 + 0x20 (小技巧,此技巧只针对常量区) 
    
    // movabsq $0x7fffffffffffffe0, %rdx
    // addq %rdx, %rdi
    
    // 0x10000A780是"0123456789ABCDEF"的真实地址
    
    // %rdi存放着字符串的真实地址
    // %rsi存放的是字符串的长度0x10
    // callq String.init
    // %rdx存放的是 字符串的真实地址 + 0x7fffffffffffffe0
    print(Mems.memStr(ofVal: &str2))//此时字符串内容已经不存在这16个字节里了
    /*这串字符串内容放在全局区*/
    

    1-2、从编程到启动APP

    /*从编程到启动APP的过程:
    OC、Swift源码 --(编译、连接)-> Mach-O二进制可执行文件(可执行程序) --(启动)-> Mach-O(代码区、全局区、常量区在这里)和动态库(Mach-O用到的动态库,程序运行过程中动态加载的) 载入内存.
    */
    
    /*不同系统转换的可执行文件不同:
         MaciOS: Mach-O
         Windows: PE
         Linux: ELF
    */
    
    /*窥探Mach-O文件:
        sp1.项目中右击Products下面的Mach-O文件,选择show in finder, 然后将其拷贝到桌面
        sp2.通过工具"MachOView fG!`s branch", File >> Open...
    
    在Mach-O文件中查看文件内存:
    比如pFile(文件偏移--也就是从文件最开始位置往下数是第几个字节)如果是0000D7B8,它在内存中其实就是0x100000000 + 0xD7B8 //0x100000000是VM address(虚拟内存地址)
    
    
    (_TEXT._cstring) 常量区
    (_DATA._data) 全局区
    */
    
    /*malloc断点查找
    */
    class Person { }
    var p = Person()//类创建好的实例肯定放堆空间
    
    
    /*总结: 
    // 字符串长度 <= 0xF, 字符串内容直接存放在str1变量的内存中
    var str1 = "0123456789"
    
    // 字符串长度 > 0xF, 字符串内容存放在__TEXT.cstring中(常量区)
    // 字符串的地址值信息存放在str2变量的后8个字节中
    var str2 = "0123456789ABCDEFGHIJ"
    
    // 由于字符串长度 <= 0xF, 所以字符串内容依然存放在str1变量的内存中
    str1.append("ABCDE")
    // 开辟堆空间
    str1.append("F")
    
    // 开辟堆空间
    str2.append("G")
    */
    
    

    1-3、dyld_stub_binder

    /*符号绑定器
        将函数的真实地址,放到我们的数据段里面去
    */
    
    /*符号的延迟绑定通过dyld_stub_binder完成*/
    
    /*jmpq *0xb31(%rip)格式的汇编指令
        占用6个字节
    */
    

    2-1、关于Array的思考

    public struct Array<Element>
    // 一个Int占8个字节
    var arr = [1,2,3,4] 
    print(MemoryLayout.stride(ofValue: arr)) // 8
    print(MemoryLayout<Array<Int>>.stride) // 8
    
    print(Mems.ptr(ofVal: &arr))//arr变量的内存地址值
    print(Mems.memStr(ofRef: arr))//arr变量的内存空间的东西
    
    /*1个Array变量占用多少内存?
            8个字节, 存放的是堆空间的内存地址值.
    
        数组中的数据存放在哪里?
            堆空间
    */
    
    /*arr数组内存空间里的字节---> 前8个字节: 未知 --> 后面8个字节放: 引用技术 --> 再后面8个字节放: 元素数量  --> 再后面8个字节放: 数组容量? --> 再后面是真实数据.
    */
    
    /*array和string行为上是值类型, 底层实际上是引用类型.*/
    

    第十四章 可选项的本质、运算符重载、扩展

    1-1、可选项的本质

    /*可选项的本质是enum类型*/
    public enum Optional<Wrapped> : ExpressibleByNilLiteral {
        case none
      case some(Wrapped)//关联值
      public init(_ some: Wrapped)
    }
    
    var age: Int? = 10
    //本质代码是👇
    var age0: Optional<Int> = Optional<Int>.some(10)
    var age1: Optional = .some(10)
    var age2 = Optional.some(10)
    var age3 = Optional(10)
    age = nil
    age3 = .none
    
    var age: Int? = nil
    //本质上等同于这么写:
    var age0 = Optional<Int>.none
    var age1: Optional<Int> = .none
    
    var age: Int? = .none
    age = 10
    age = .some(20)
    age = nil
    
    /*可选类型支持switch, 用switch判断可选类型*/
    switch age {
    case let v?:
      print("some", v)
    case nil:
      print("none")
    }
    
    // 本质上是:
    switch age {
    case let .some(v):
      print("some", v)
    case .none:
      print("none")
    }
    
    //等价于:
    if let v = age {
      print("some", v)
    } else {
      print("none")
    }
    

    1-2、多重可选项

    var age_: Int? = 10
    var age: Int?? = age_
    age = nil
    
    //本质上是👇
    var age0 = Optional.some(Optional.some(10))
    age0 = .none
    var age1: Optional<Optional> = .some(.some(10))
    age1 = .none
    
    var age: Int?? = 10
    var age0: Optional<Optional> = 10
    

    2-1、溢出运算符(Overflow Operator)

    /*Swift的算数运算符出现溢出时会抛出运行时错误*/
    print(Int8.min)// -128
    print(Int8.max)// 127
    print(UInt8.min)// 0
    print(UInt8.max)// 255
    //UInt的取值范围是0~255
    
    var v: UInt8 = UInt8.max
    v += 1 //报错
    
    /*Swift有溢出运算符(&+、&-、&*), 用来支持溢出运算. (超出最大/最小值后会循环回去)*/
    var v1: UInt8 = UInt8.max
    v2 = v1 &+ 1 // 0
    
    var v1: UInt8 = UInt8.min
    v2 = v1 &- 1 // 255
    
    var v1: UInt8 = UInt8.max
    v2 = v1 &* 2 // 254 解析:相当于 v1 &+ v1,即255 &+ 255 > 254
    

    2-2、运算符重载(Operator Overload)

    /*类、结构体、枚举可以为现有的运算符提供自定义的实现, 这种操作叫做: 运算符重载*/
    struct Point {
      var x: Int, y: Int
    }
    
    // 重载: 函数名相同, 功能不一样
    func + (p1: Point, p2: Point) -> Point {
      Point(x: p1.x +p2.x, y: P1.y + p2.y)
    }
    
    let p = Point(x: 10, y: 20) + Point(x: 11, y: 22)
    porint(p) // Point(x: 21, y: 42)
    var p1 = Point(x: 10, y: 20)
    var p2 = Point(x: 10, y: 20)
    var p3 = Point(x: 10, y: 20)
    let p4 = p1 + p2 + p3 // 重载的结合性
    print(p4)
    
    //优化上面的代码: 重载建议写到类型里面去.如下👇
    struct Point {
      var x: Int, y: Int
      static func + (p1: Point, p2: Point) -> Point {
        Point(x: p1.x + p2.x, y: P1.y + p2.y)
      }
      static func - (p1: Point, p2: Point) -> Point {//中缀运算符
        Point(x: p1.x - p2.x, y: P1.y - p2.y)
      }
      static prefix func - (p1: Point) -> Point {//前缀运算符
        Point(x: -p1.x, y: -P1.y)
      }
      static func += (p1: inout Point, p2: Point) {
        p1 = p1 + p2
      }
      static prefix func ++ (p: inout Point) -> Point {
        p += Point(x: 1, y: 1)
        return p
      }
      static postfix func ++ (p: inout Point) -> Point {//后缀运算符
        let tmp = p
        p += Point(x: 1, y: 1)
        return tmp
      }
      static func == (p1: Point, p2: Point) -> Bool {
        (p1.x == p2.x) && (p1.y == p2.y)
      }
    }
    
    var p1 = Point(x: 10, y: 20)
    var p2 = Point(x: 10, y: 20)
    var p3 = -p2//前缀运算符
    print(p3)
    p1 += p2
    print(p1)
    

    2-2、Equatable

    /*要想得知2个实例是否等价, 一般做法是遵守Equatable协议,重载 == 运算符
        与此同时,等价于重载了 != 运算符
    */
    class Person : Equatable { //因为是重载运算符, 可省略: Equatable 
      var age = 0
      init(age: Int) {
        self.age = age
      }
      //定义一个规则, 只要发现这两个人的年龄相等,就认为这两个人等价
      static func == (lhs: Person, rhs: Person) -> Bool {
        lhs.age == rhs.age
      }
    }
    var p1 = Person(age: 10)
    var p2 = Person(age: 10)
    print(p1 == p2)
    print(p1 != p2)
    
    
    /*只要遵守了Equatable协议, 说明可以用等价运算符来进行比较的,也就具备了等价性判断*/
    func eqauls<T : Equatable>(_ t1: T, _ t2: T) -> Bool {
      t1 == t2
    } 
    equals(Person(),Person())
    
    /*Swift为以下类型提供默认的 Equatable 实现:*/
    // 1.没有关联类型的枚举
    enum Answer {
        case wrong
      case right
    }
    var s1 = Answer.wrong
    var s2 = Answer.right
    print(s1 == s2)
    // 2.只拥有遵守 Equatable 协议关联类型的枚举
    class Cat {}
    enum Answer : Equatable {
        case wrong(Int, String)//Int和String内部默认遵守Equatable协议
      case right(Cat)
    }
    var s1 = Answer.wrong(10, "Jack")
    var s2 = Answer.wrong(20, "Rose")
    print(s1 == s2)
    var s3 = Answer.right(Cat())
    var s4 = Answer.right(Cat())
    print(s3 == s4)//报错: 因为Cat没有遵守Equatable协议
    // 3.只拥有遵守 Equatable 协议存储属性的结构体.(类不包括)
    struct Point : Equatable {
      var x = 0 , y = 0
      static func == (p1: Point, p2: Point) -> Bool {
        (p1.x == p2.x) && (p1.y == p2.y)
      }
    }
    
    /*引用类型比较存储的地址值是否相等(是否引用着同一个对象),使用恒等运算符 ===、!==*/
    

    2-3、Comparable

    /*要想比较2个实例的大小, 一般做法是: 
        遵守 Comparable 协议
        重载相应的运算符
    */
    // score大的比较大, 若score相等, age小的比较大
    struct Student : Comparable {
      var age: Int
      var score: Int
      init(score: Int, age: Int) {
        self.score = score
        self.age = age
      }
      static func < (lhs: Student, rhs: Student) -> Bool {
        (lhs.score < rhs.score) || (lhs.score == rhs.score && lhs.age > rhs.age)
      }
      static func > (lhs: Student, rhs: Student) -> Bool {
        (lhs.score > rhs.score) || (lhs.score == rhs.score && lhs.age < rhs.age)
      }
      static func <= (lhs: Student, rhs: Student) -> Bool {
        !(lhs > rhs)
      }
      static func >= (lhs: Student, rhs: Student) -> Bool {
        !(lhs < rhs)
      }
    }
    
    var stu1 = Student(score: 100, age: 20)
    var stu2 = Student(score: 98, age: 18)
    var stu3 = Student(score: 100, age: 20)
    print(stu1 > stu2) // true
    print(stu1 >= stu2) // true
    print(stu1 >= stu3) // true
    print(stu1 <= stu3) // true
    print(stu2 < stu1) // true
    print(stu2 <= stu1) // true
    

    2-4、自定义运算符(Custom Operator)

    /*可以自定义新的运算符: 在全局作用域使用operator进行声明
      prefix operator 前缀运算符
      postfix operator 后缀运算符
      infix operator 中缀运算符 : 优先级组
    */
    
    /*
    precedencegroup 优先级组 {
        associativity: 结合性(left\right\none)
        higherThan: 比谁的优先级高
        lowerThan: 比谁的优先级低
        assignment: true 代表在可选链操作中拥有跟赋值运算符一样的优先级
    }
    */
    
    /*
    prefix operator +++
    infix operator +- : PlusMinusPrecedence
    percedencegroup PlusMinusPrecedence {
        associativity: none //结合性 a1 + a2 + a3, 为none则表示不能结合使用
        higherThan: AdditionPrecedence //比加号的优先级高
        lowerThan: MultiplicationPrecedence //比减号的优先级低
        assignment: true //
    }
    */
    /*Apple文档参考:              https://developer.apple.com/documentation/swift/swift_standard_library/operator_declarations
    
    https://docs.swift.org/swift-book/ReferenceManual/Declarations.html#ID380   
    */
    
    //例子如下:
    prefix operator +++
    prefix func +++ (_ i: inout Int) {
      i += 2
    }
    var age = 10
    +++age
    
    //解释assignment: true
    struct Point {
      var x = 0, y = 0
      static func +- (p1: Point, p2: Point) -> Point {
        Point(x: p1.x + p2.x, y: p1.y - p2.y)
      }
    }
    class Person? = Person()
    p?.point +- Point(x: 10, y:20) //如果p有值, 才会执行后面的运算
    

    3-1、扩展(Extension)

    /*
    Swift中的扩展, 有点儿类似于OC中的分类( Category )
    扩展可以为枚举、结构体、类、协议添加新功能
        可以添加方法、计算属性、下标、(便捷)初始化器、嵌套类型、协议等等
    扩展不能办到的事情
        不能覆盖原有的功能
        不能添加存储属性, 不能向已有的属性添加属性观察器
        不能添加父类
        不能添加指定初始化器, 不能添加反初始化器
        ...
    */
    extension Double {// 扩展增加计算属性
      var km: Double { self * 1_000.0 }
      var m: Double { self }
      var dm: Double { self / 10.0 }
      var cm: Double { self / 100.0 }
      var mm: Double { self / 1_000.0 }
    }
    
    extension Array {// 扩展增加方法: 下标判断,发现数组越界了,就返回nil
      subscript(nullable idx: Int) -> Element? {//原有类的泛型,在扩展中一样可用.比如: Element代表数组中的元素
        if(startIndex..<endIndex).contains(idx) {
          return self[idx]
        }
        return nil
      }
    }
    var arr: Array<Int> = [10, 20, 30]
    print(arr[nullable: 10] ?? 0)
    
    extension Int {
      func repetitions(task: () -> Void) {
        for _ in 0..<self { task() }
      }
      mutating func square() -> Int {// 扩展结构体mutating的方法
        self = self * self
        return self
      }
      enum Kind { case negative, zero, positive }
      var kind: Kind {// 扩展嵌套类型: 判断复数、0、正数
        switch self {
          case 0: return .zero
          case let x where x > 0 : return .positive
          default: return .negative
        }
      }
      subscript(digitIndex: Int) -> Int {
        var decimalBase = 1
        for _ in 0..<digitIndex { decimalBase *= 10 }
        return (self / decimalBase) % 10
      }
    }
    
    3.repeats {//重复打印3次
      print(1)
    }
    var age = 456
    print(age[0])//获取下标对应数字
    

    3-2、扩展 协议、初始化器

    class Person {
      var age: Int
      var name: String
      init(age: Int, name: String) {
        self.age = age
        self.name = name
      }
    }
    extension Person : Equatable { //给Person扩展协议
      static func == (left: Person, right: Person) -> Bool {
        left.age == right.age && left.name == right.name
      }
      convenience init() {//给类扩展便捷初始化器,指定初始化器不能给类扩展
        self.init(age: 0, name: "")
      }
    }
    
    
    struct Point {
      var x: Int = 0
      var y: Int = 0
    }
    extension Point {
      init(_ point: Point) {//给结构体扩展初始化器(注意:便捷初始化器和指定初始化器是类才有的概念.)
        self.init(x: point.x, y:point.y)
      }
    }
    var p1 = Point()//编译器自带初始化器
    var p2 = Point(x: 10)//编译器自带初始化器
    var p3 = Point(y: 20)//编译器自带初始化器
    var p4 = Point(x: 10, y: 20)//编译器自带初始化器
    var p5 = Point(p4)//扩展初始化器
    /*
        如果希望自定义初始化器的同时, 编译器也能够生成默认初始化器, 那么可以在扩展汇总编写自定义初始化器
        required初始化器也不能写在扩展中
    */
    
    protocol Runnable {
      init(age: Int)
    }
    class Person {}
    extension Person : Runnable {
      required init(age: Int){//报错: required初始化器必须直接声明在'Person'类里面
        
      }
    }
    

    3-3、协议

    /*如果一个类型已经实现了协议的所有要求, 但是还没有声明它遵守了这个协议, 那么可以通过扩展来让它遵守这个协议*/
    protocol TestProtocol {
      func test()
    }
    class TestClass {
      func test() {
        print("test")
      }
    }
    extension TestClass : TestProtocol {}
    
    /// 例子: 编写一个函数, 判断一个整数是否为奇数
    //第一种做法:
    func isOdd<T: BinaryInteger>(_ i: T) -> Bool { i % 2 != 0 }//只要是整数的共同点是: 都遵守BinaryInteger协议
    print(isOdd(4))
    
    //第二种做法: 给BinaryInteger协议扩展方法
    extension BinaryInteger {
        func isOdd() -> Bool { self % 2 != 0 }
    }
    print(10.isOdd())
    print((-3).isOdd())
    
    /*扩展可以给协议提供默认实现, 也间接实现[可选协议]的效果
        扩展可以给协议扩充[协议中从未声明过的方法]
    */
    protocol TestProtocol {
      func test1()
    }
    extension : TestProtocol {
      func test1() {
        print("TestProtocol test1")
      }
      func test2() {
        print("TestProtocol test2")
      }
    }
    
    //场景一
    class TestClass : TestProtocol {} //因为上面扩展中实现了TestProtocol的方法, 所以不会报错.
    var cls = TestClass()
    cls.test1() // TestProtocol test1
    cls.test2() // TestProtocol test2
    var cls2 : TestProtocol = TestClass()
    cls2.test1() // TestProtocol test1
    cls2.test2() // TestProtocol test2
    
    //场景二
    class TestClass : TestProtocol {
      func test1() { print("TestClass test1") } 
      func test2() { print("TestClass Test2") }
    }
    var cls = TestClass()//优先调用自己的实现
    cls.test1() // TestClass test1
    cls.test2() // TestClass test2
    var cls2 : TestProtocol = TestClass()//编译器一旦发现它是TestProtocol类型,它就知道一旦你调用test2, test2可能在实例里面是不存在的,它就直接当做它不存在.所以他就不去实例里面找了, 优先去协议里面找
    cls2.test1() // TestClass test1 解析: 协议里面声明了这个方法, 那么这个方法必然要实现, 所以这里虽然声明成实例类型, 调用test1, 肯定优先实例里面找.我协议里面有,你实例里面肯定有的
    cls2.test2() // TestProtocol test2 解析:如果你要调用一个方法, 是协议声明里没有的, 而这里又定义成协议类型, 它会认为你的实例里面没有test2, 它就直接去协议扩展里面优先找到test2了,所以是这样一个结果
    

    3-4、协议里的泛型

    class Stack<E> {
      var elements = [E]()
      func push(_ element: E){
        elements.append(element)
      }
      func pop() -> E { elements.removeLast() }
      func size() -> Int { elements.count }
    }
    // 扩展中依然可以使用原类型中的泛型类型
    extension Stack {
        func top() -> E { elements.last! }
    }
    // 符合条件才扩展
    extension Stack : Equatable where E: Equatable {
        static func == (left: Stack, right: Stack) -> Bool {
        left.elements == right.elements
      }
    }
    

    第十五章 访问控制、内存管理

    1-1、遵守CustomStringConvertible、CustomDebugStringConvertible协议,都可以自定义实例的打印字符串

    class Person : CustomStringConvertible, CustomDebugStringConvertible {
      var age = 0
      var description: String { "person_\(age)" }
      var debugDescription: String { "debug_person_\(age)"}
    }
    var person = Person()
    print(person) // person_0
    debugPrint(person) //debug_person_0
    
    /*
    print调用的是CustomStringConvertible协议的description
    debugPrint、po调用的是CustomDebugStringConvertible协议的debugDescription
    */
    

    1-2、Self

    /*Self一般用作返回值类型, 限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)*/
    protocol Runnable {
      func test() -> Self
    }
    class Person : Runnable {
      reuqired init() {}
      func test() -> Self { type(of: self).init() }
    }
    class Student : Person {}
    
    var p = Person()
    // Person
    print(p.test())
    
    var stu = Student()
    // Student
    print(stu.test())
    
    /*另一个用途:  Self代表当前类型*/
    class Person {
      var age = 1
      static var count = 2
      func run(){
        print(self.age) //1
        print(Self.count) //2
      }
    }
    

    1-3、错误处理--断言( assert )

    /*
        很多编程语言都有断言机制: 不符合指定条件就抛出运行时错误, 常用于调试(Debug)阶段的条件判断
        默认情况下, swift的断言只会在Debug模式下生效, Release模式下会忽略
    */
    func divide(_ v1: Int, _ v2: Int) -> Int {
      assert(v2 != 0, "除数不能为0")//条件成立的话就会继续往下进行, 条件不成立, 就会抛出异常,导致崩溃,并且抛出后面的信息.
      return v1 / v2
    }
    print(divide(20, 0))
    /*与自定义错误的区别:
        如果希望抛出的错误能被do-catch捕捉到, 而且捕捉到后还能做出相应的反应, 不至于崩溃. 就用自定义错误, 自己抛出;
        如果希望在调试阶段, 就发现条件不对, 就崩溃, 不能被do-catch拦截,就用assert.
    */
    
    /*
        增加Swift Flags修改断言的默认行为(Build Settings >> Other Swift Flags)
            -assert-config Release : 强制关闭断言
            -assert-config Debug : 强制开启断言
    */
    

    1-4、fatalError

    /*
    如果遇到严重问题, 希望结束程序运行时, 可以直接使用fatalError函数抛出错误(这是无法通过do-catch捕捉的错误)
        使用fatalError函数, 就不需要再写return
    */
    func test(_ num: Int) -> Int {
        if num >= 0 {
        return 1
      }
      fatalError("num不能小于0")//闪退
    }
    /*与assert相似, 对比assert断言:
        不区分Release和Debug, 都好使, 都闪退.
    */
    
    /*在某些不得不实现、但不希望别人调用的方法, 可以考虑内部使用fatalError函数*/
    class Person { required init() {} }
    class Student : Person {
      required init() { fatalError("don`t call Student.init")}
      init(score: Int) {}
    }
    var stu1 = Student(score: 98)
    var stu2 = Student()
    

    2-1、访问控制 (Access Control)

    /*
        在访问权限控制这块, Swift提供了5个不同的访问级别 (以下是从高到低排列, 实体指被访问级别修饰内容)
            > open: 允许在定义实体的模块、其他模块中访问, 允许其他模块进行继承、重写( open只能用在类, 类成员上)
            > public: 允许在定义实体的模块、其他模块中访问, 不允许其他模块进行继承、重写
            > internal : 只允许在定义实体的模块中访问, 不允许在其他模块中访问.
            > fileprivate: 只允许在定义实体的源文件中访问
            > private: 只允许在定义实体的封闭声明中访问
    
    绝大部分实体默认都是internal级别
    */
    
    /*访问级别的使用准则
    一个实体不可以被更低访问级别的实体定义, 比如:
        变量/常量类型 >= 变量/常量
        参数类型、返回值类型 >= 函数
        父类 >= 子类
        父协议 >= 子协议
        原类型 >= typealias
        原始值类型、关联值类型 >= 枚举类型
        定义类型A时用到的其他类型 >= A
        ...
    */
    
    //例子: 变量/常量类型 >= 变量/常量
    fileprivate class Person { }
    internal var person: Person //报错: 类型级别<变量级别 导致的
    

    2-2、元组类型的访问级别

    /*元组类型的访问级别是所有成员类型最低的那个*/
    internal struct Dog {}
    fileprivate class Person {}
    
    // (Dog, Person)的访问级别是fileprivate
    fileprivate var data1: (Dog, Person)
    private var data2: (Dog, Person)
    

    2-3、泛型类型的访问级别

    /*泛型类型的访问级别是 类型的访问级别 以及 所有泛型类型参数的访问级别 中最低的那个*/
    internal class Car {}
    fileprivate class Dog {}
    public class Person<T1, T2>{ }
    
    // Person<Car, Dog>访问级别是fileprivate
    fileprivate var p = Person<Car, Dog>()
    

    2-4、成员、嵌套类型

    /*
    类型的访问级别会影响成员(属性、方法、初始化器、下标)、嵌套类型的默认访问级别.
        一般情况下, 类型为private或fileprivate, 那么成员/嵌套类型默认也是private或fileprivate
        一般情况下, 类型为internal或public, 那么成员/嵌套类型默认是internal
    */
    public class PublicClass {
        public var p1 = 0
      fileprivate func f1() {} //fileprivate
      private func f2() {} //private
    }
    class InternalClass {//internal
      var p = 0 //internal
      fileprivate func f1() {}//fileprivate
      private func f2() {} //private
    }
    
    fileprivate class FilePrivateClass { // fileprivate
      func f1() {} //fileprivate
        private func f2() {} //private
    }
    private class PrivateClass { // private
      func f() {} //private
    }
    /*子类重写的成员访问级别必须 >= 父类的成员访问级别*/
    
    /// 下面代码能否编译通过?
    private class Person {}
    fileprivate class Student : Person {} //场景不同,决定是否报错
    
    private struct Dog { //此时private相当于fileprivate,里面的成员也相当于fileprivate
      var age: Int = 0
      func run() {}
    }
    fileprivate struct Person {
      var dog: Dog = Dog()
      mutating func walk() {
        dog.run()
        dog.age = 1
      }
    }
    
    private Struct Dog {
        private var age: Int = 0
      private func run() {}
    }
    fileprivate struct Person {//报错
      var dog: Dog = Dog()
      mutating func walk() {
        dog.run()
        dog.age = 1
      }
    }
    /*直接在全局作用域下定义的private等价于fileprivate*/
    

    2-4-5、成员的重写

    /*
        ①子类重写成员的访问级别必须 >= 子类的访问级别, 或者 >= 父类被重写成员的访问级别
    
        ②父类的成员不能被成员作用域外定义的子类重写
    */
    
    // ②
    public class Person {
      private var age: Int = 0
    }
    public class Student : Person {
      override var age : Int{//报错
        set {}
        get {10}
      }
    }
    // 改为下面这样写👇
    public class Person {
      private var age: Int = 0
      public class Student : Person {
        override var age: Int {
          set {}
          get {10}
        }
      }
    }
    
    // ① 成员访问级别跟协议很像 , 访问级别 >= 子类访问级别或在父类所重写成员 两个中最小的一个就行了
    class Person {
      internal func run() { }
    }
    fileprivate class Student : Person {
      fileprivate override func run() { }
    }
    

    2-5、getter、setter

    /*getter、setter默认自动接收他们所属环境的访问级别
        可以给setter单独设置一个比getter更低的访问级别, 用以限制写的权限
    */
    fileprivate(set) public var num = 10
    class Person {
      private(set) var age = 0
      fileprivate(set) public var weight: Int {
        set {}
        get { 10 }
      }
      internal(set) public subscript(index: Int) -> Int {
        set {}
        get { index }
      }
    }
    

    2-6、初始化器

    /*
        ①如果一个public类想在另一个模块调用编译生成的默认无参初始化器, 必须显示提供public的无参初始化器
            因为public类的默认初始化器是internal级别
        ②required初始化器 >= 它的默认访问级别
        ③如果结构体有private/fileprivate的存储实例属性,那么它的成员初始化器也是private/fileprivate
            否则默认就是internal
    */
    
    // ①
    // 在动态库xxx.dylib中
    public class Person {
      //如果这个类型默认是public,那么内部的所有成员默认都是internal
      public init() { //这里要写public,才能被其他模块调用
      }
    } 
    // 在TestSwift中
    var p = Person() //报错
    

    2-7、枚举类型的case

    /*不能给enum的每个case单独设置访问级别
        每个case自动接收enum的访问级别
        public enum定义的case也是public
    */
    public enum Season {
        case spring, summer, autumn winter//public
    }
    

    2-8、协议

    /*
    ①协议中定义的要求自动接收协议的访问级别, 不能单独设置访问级别
        public协议定义的要求也是public
    
    ②协议实现的访问级别必须 >= 类型的访问级别, 或者 >= 协议的访问级别
    */
    
    //①
    public protocol Runnable {
        func run() //public
    }
    
    //②
    fileprivate protocol Runnable {
      func run()
    }
    public class Person : Runnable {
      fileprivate func run() {
        
      }
    }
    
    /*下面代码能不能编译通过?*/
    public protocol Runnable {
      func run() // public
    }
    public class Person : Runnable {
      func run() {}//internal 编译报错
    }
    

    2-9、扩展

    /*如果有显示设置扩展的访问级别, 扩展添加的成员自动接收扩展的访问级别*/
    class Person {}
    fileprivate extension Person {
      func run() {}
    }
    /*如果没有显示设置扩展访问级别,扩展添加的成员默认的访问级别, 跟直接在类型中定义的成员一样*/
    /*可以单独给扩展添加的成员设置访问级别*/
    /*不能给用于遵守协议的扩展显式设置扩展的访问级别*/
    
    /*
    在同一文件的扩展, 可以写成类似多个部分的类型声明
        在原本的声明中声明一个私有成员, 可以在同一文件的扩展中访问它
        在扩展中声明一个私有成员, 可以在同一文件的其他扩展中、原本声明中访问它
    */
    // 在同一个文件中
    public class Person {
      private func run0()
      private func eat0() {
        run1()
      }
    }
    extension Person {
      private func run1() {}
      private func eat1() {
        run0()
      }
    }
    extension Person {
      private func eat2() {//相当于直接写在了类型里
        run1()
      }
    }
    

    2-10、将方法赋值给var/let

    /*方法也可以像函数那样, 赋值给一个let或者var*/
    struct Preson {
      var age: Int
      static func run(_ v: Int) { print("static func run", v) }
    }
    // var fn1: (Person) -> (int) -> () = Person.run
    var fn1 = Person.run //实例方法赋值给fn1
    var fn2 = fn1(Person(age: 10)) //带有person实例的run方法
    fn2(20) //拿出实例调用run, 然后传个20
    //等同于下面👇
    Person(age:10).run(20)
    
    struct Preson {
      var age: Int
      static func run(_ v: Int) { print("static func run", v) }
    }
    var fn  = Person.run
    fn(20)
    

    3-1、内存管理

    /*
    跟OC一样, Swift也是采取基于引用计数的ARC内存管理方案(针对堆空间)
    
    Swift的ARC中有3种引用
        1.强引用(strong reference) : 默认情况下, 引用都是强引用
        2.弱引用(weak reference) : 通过weak定义弱引用
            必须是可选类型的var, 因为实例销毁后, ARC会自动将弱引用设置为nil.
            ARC自动给弱引用设置为nil时, 不会触发属性观察器
        3.无主引用(unowned reference) : 通过unowned定义无主引用
            不会产生强引用, 非可选类型, 实例销毁后仍然存储着实例的内存地址(类似于OC中的unsafe_unretained)
            试图在实例销毁后访问无主引用, 会产生运行时错误(野指针)
    */
    
    class Person {
      weak var dog: Dog? {
        willSet {}
        didSet {}
      }
        deinit {
        print("Person.deinit")
      }
    }
    
    print(1)
    weak var p: Person? = Person()
    print(2)
    print(3)
    
    var p = Person()
    p.dog = nil //不会触发属性观察器
    

    3-2、weak、unowned的使用限制

    /*weak、unowned只能用在类实例上面*/
    protocol Livable : AnyObject {}//写上:AnyObject,代表将来这个协议只能被类遵守
    class Person  {}
    
    weak var p0: Person?
    weak var p1: AnyObject?
    weak var p2: Livable?
    
    unowned var p10: Person?
    unowned var p11: AnyObject?
    unowned var p12: Livable?
    

    3-3、Autoreleasepool

    //自动释放池
    public func autoreleasepool<Result>(invoking body: () throws -> Result) rethrows -> Result
    
    //使用
    autoreleasepool {
        let p = MJPerson(age: 20, name: "Jack")
      p.run()
    }
    

    3-4、循环引用(Reference Cycle)

    /*
    weak、unowned 都能解决循环引用的问题, unowned 要比 weak 少一些性能消耗
        在生命周期中可能会变为 nil 的使用 weak
        初始化赋值后再也不会变为 nil 的使用 unowned
    */
    

    3-5、闭包的循环引用

    /*
        闭包表达式默认会对用到的外层对象产生额外的强引用(对外层对象进行了retain操作)
        下面代码会产生循环引用, 导致Person对象无法释放(看不到Person的deinit被调用)
    */
    class Person {
      var fn: (() -> ())?
      func run() { Print("run") }
      deinit{ print("deinit") }
    }
    func test() {
      let p = Person()
      p.fn = { p.run() }//产生了强引用
    }
    test()
    
    /*在闭包表达式的捕获列表声明weak或unowned引用,解决循环引用问题*/
    p.fn = { 
      [weak p] in //[weak p]是捕获列表
      p?.run()//弱引用必须是可选类型, 所以p前加?
    }
    
    p.fn = {
      [unowned p] in
      p.run()//unowned默认是非可选类型, 所以要去掉p前的?
    }
    
    p.fn = {
      [weak wp = p, unowned up = p, a = 10 + 20] in //在捕获列表里定义新的名称
      wp?.run()
    }
    
    /*如果想在定义闭包属性的同时引用self, 这个闭包必须是lazy的(因为在实例初始化完毕之后才能引用self)*/
    class Person {
      lazy var fn: (() -> ()) = {//lazy 意味着调用之前这里是空的
        [weak self] in
        self?.run()//要等初始化之后才能执行
      }//声明一个闭包表达式
      func run() { print("rum") }
      deinit { print("deinit") }
    }
    var p = Person()
    p.fn()
    /*上面的闭包fn内部如果用到了实例成员(属性、方法)
        编译器会强制要求明确写出self
    */
    
    /*如果lazy属性是闭包调用的结果,那么不用考虑循环引用的问题(因为闭包调用后, 闭包的生命周期就结束了)*/
    class Person {
      var age: Int = 0
      lazy var getAge: Int = {
        self.age
      }()//声明一个闭包表达式,马上调用
      deinit { print("deinit") }
    }
    func test() {
      var p = Person()
        print(p.getAge)
    }
    test()
    

    3-6、@escaping

    /*非逃逸闭包、逃逸闭包, 一般都是当做参数传递给函数*/
    /*非逃逸闭包: 闭包调用发生在函数结束前, 闭包调用在函数作用域内*/
    /*逃逸闭包: 闭包有可能在函数结束后调用, 闭包调用逃离了函数的作用域, 需要通过@escaping声明*/
    
    import Dispatch //导入GCD的模块
    typealias Fn = () -> ()
    
    // fn是非逃逸闭包
    func test1(_ fn: Fn) { fn() }//在函数内部调用闭包,所以是非逃逸闭包
    
    // fn是逃逸闭包: 有可能在函数外面调用闭包, 所以是逃逸闭包
    var gFn: Fn?
    func test2(_ fn: @escaping Fn) { gFn = fn }
    
    // fn是逃逸闭包 : 有可能在函数外面调用闭包,存在一定风险, 所以是逃逸闭包
    func test3(_ fn: @escaping Fn) {
      DispatchQueue.global().sync {//全局队列, 异步调用
        fn()
      }
    }
    
    class Person {
        var fn: Fn 
      // fn是逃逸闭包
      init(fn: @escaping Fn) {
        self.fn = fn
      }
      func run() {
        // DispatchQueue.global().sync也是一种逃逸闭包
        // 它用到了实例成员(属性、方法), 编译器会强制要求明确写出self
        DispatchQueue.global().sync {
            self.fn()
        }
      }
    }
    

    3-7、逃逸闭包的注意点

    /*逃逸闭包不可以捕获inout参数*/
    typealias Fn = () -> ()
    func other1(_ fn: Fn) { fn() }
    func other2(_ fn: @escaping Fn) { fn() }
    func test(value: inout Int) -> Fn {
      other1 { value += 1 }
     
      other2 { value += 1 }// 报错error: 逃逸闭包不能捕获inout参数
      
      func plus() { value += 1 }
      return plus// 报错error: 这里相当于也返回了一个闭包,逃逸闭包不能捕获inout参数
    }
    

    第十六章 内存访问冲突、指针

    1-1、do

    /*除了do-catch外, 还可以使用 do 实现局部作用域*/
    do {
      let dog1 = Dog()
      dog1.age  =10
      dog1.run()
    }
    
    do {
      let dog2 = Dog()
      dog2.age  = 10
      dog2.run()
    }
    

    2-1、内存访问冲突 ( Conflicting Access to Memory)

    /*
    内存访问冲突会导致编译报错或运行时报错.
    
    内存访问冲突会在两个访问满足下列条件时发生 :
        至少一个是写入操作
        他们访问的是同一块内存
        它们的访问时间重叠(比如在同一个函数内)
    */
    
    // 不存在内存访问冲突
    func plus(_ num: inout Int) -> Int { num + 1 }
    var number = 1
    number = plus(&number)
    
    // 存在内存访问冲突
    // Simulations accesses to 0x0, but modification requires exclusive access
    var step = 1
    func increment(_ num: inout Int) { num += step }
    increment(&step)
    
    // 解决内存访问冲突
    var step = 1
    func increment(_ num: inout Int) { num += step }
    // increment(&step)
    var copyOfStep = step
    increment(&copyOfStep)
    step = copyOfStep
    

    2-2、下面是几个内存访问冲突的情况:

    // 在全局作用域中写以下代码:
    /*①*/
    func balance(_ x: inout Int, _ y: inout Int) {
      let sum = x + y
      x = sum / 2
      y = sum - x
    }
    var num1 = 42
    var num2 = 30
    balance(&num1, &num2) // OK
    balance(&num1, &num1) //Error 
    
    /*②*/
    struct Player {
        var name: String
      var health: Int
      var energy: Int
      mutating func shareHealth(with teamate: inout Player) {
        balance(&teamate.health, &health)
      }
    }
    var oscar = Player(name: "Oscar", health: 10, energy: 10)
    var maria = Player(name: "Maria", health: 5, energy: 10)
    oscar.shareHealth(with: &maria) //Ok
    oscar.shareHealth(with: &oscar) //Error
    
    /*③ 重叠访问结构体属性*/
    var tulpe = (health: 10, energy: 20)
    //Error
    balance(&tulpe.health, &tulpe.energy)//重叠访问结构体的属性
    
    var holly = Player(name: "Holly", health: 10, energy: 10)
    // Error
    balance(&holly.health, &holly.energy)//重叠访问结构体的属性
    

    2-3、内存访问冲突

    /*
    如果下面的条件可以满足, 就说明重叠访问结构体的属性是安全的
        你访问的实例存储属性, 不是计算属性或者类属性
        结构体是局部变量而非全局变量
        结构体要么没有被闭包捕获要么只被非逃逸闭包捕获
    */
    // OK
    func test() {
      var tulpe = (health: 10, energy: 20)
      balance(&tulpe.health, &tulpe.energy)
    
      var holly = Player(name: "Holly", health: 10, energy: 10)
      balance(&holly.health, &holly.energy)
    }
    test()
    

    3-1、指针

    /*
    Swift中也有专门的指针类型, 这些都被定性为 "Unsafe"(不安全的), 常见的有以下4种类型
        UnsafePointer<Pointee> 类似于 const Pointee * //只通过指针可以访问内存, 不可以通过指针修改内存
        UnsafeMutablePointer<Pointee>  类似于 Pointee * //可以通过指针访问内存, 可以通过指针修改内存
        UnsafeRawPointer 类似于 const void * // 不确定指向什么类型的数据
        UnsafeMutableRawPointer 类型于 void *
    */
    var age = 10
    func test1(_ ptr: UnsafeMutablePointer<Int>) {// int *
      ptr.pointee += 20 //带泛型的指针类型, 直接通过.pointee就可以改变值
    }
    func test2(_ ptr: UnsafePointer<Int>) {// const int *
      print("test2", ptr.pointee)// 访问指针所指向内存的值
    }
    test2(&age) //10
    test1(&age) //30
    print(age) //30
    
    var age2 = 10
    func test3(_ ptr: UnsafeRawPointer) { // const void *
      print("test3", ptr.load(as: Int.self)) //加载
    }
    func test4(_ ptr: UnsafeMutableRawPointer) {// void *
      ptr.storeBytes(of: 30, as: Int.self) //存储
    }
    test3(&age2) //10
    test4(&age2) //30
    print(age2) //30
    

    3-2、指针的应用示例

    import Foundation
    var arr = NSArray(object: 11, 22, 33, 44)
    arr.enumerateObjects { (obj, idx, stop) in
        print(idx, obj)
      if idx == 2 { // 下标为2就停止遍历
        stop.pointee = true
      }
    }
    
    var arr = NSArray(object: 11, 22, 33, 44)
    for (idx, onj) in arr.enumerated() {
        print(idx, obj)
      if idx == 2 { // 下标为2就停止遍历
        break
      }
    }
    

    3-3、获取指向某个变量的指针

    import Foundation
    var age = 10
    var ptr = withUnsafeMutablePointer(to: &age) { $0 }
    
    var ptr2 = withUnsafePointer(to: &age) { $0 }
    var ptr2 = withUnsafePointer(to: &age){ (p) -> UnsafePointer<Int> in
        return p                                    
    }
    // 仿写底层实现上面👆
    func withUnsafePointer<Result, T>(to: UnsafePointer<T>, body:(UnsafePointer<T>) -> Result) -> Result {
        return body(to)
    })
    
    /*总结:*/
    var age = 11
    var ptr1 = withUnsafeMutablePointer(to: &age) { $0 }
    var ptr2 = withUnsafePointer(to: &age) { $0 }
    ptr1.pointee = 22
    print(ptr2.pointee) // 22
    print(age) // 22
    
    var ptr3 = withUnsafeMutablePointer(to: &age) { UnsafeMutableRawPointer( $0 ) }//自定义返回值
    var ptr4 = withUnsafePointer( to: &age) { UnsafeRawPointer($0) }
    ptr3.storeBytes(of: 33, as: Int.self)
    print(ptr4.load(as: Int.self))// 33
    print(age)//33
    

    3-4、获得指向堆空间实例的指针

     class Person {
       var age: Int
       init(age: Int) {
         self.age = age
       }
     }
    var peron = Person(age: 21)
    var ptr1 = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
    var personObjAddress = ptr1.load(as: UInt.self)
    var ptr2 = UnsafeMutableRawPointer(bitPattern: personObjAddress)
    print(ptr2) // 0x0000000100600fd80
    print(Mems.ptr(ofRef: person))
    
    /*总结:*/
    class Person {}
    var person = Person()
    var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }
    var heapPtr = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))
    print(heapPtr!)
    

    3-5、创建指针

    import Foundation
    
    var ptr = UnsafeRawPointer(bitPattern: 0x100001234)
    
    var ptr = malloc(16) //创建指针: 分配堆空间16个字节大小
    // 存
    ptr?.storeBytes(of: 11, as: Int.self)
    ptr?.storeBytes(of: 22, toByteOffset: 8, as: Int.self)
    // 取
    print((ptr?.load(as: Int.self))!) //11
    print((ptr?.load(fromByteOffset: 8, as: Int.self))!) //22
    free(ptr)//销毁: 释放堆空间
    
    var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)//创建指针: 申请16个字节
    ptr.storeBytes(of: 11, as: Int.self)//存储
    ptr.advanced(by: 8).storeBytes(of: 22, as: Int.self)//存储
    //取
    print(ptr.load(as: Int.self)) //11
    print(ptr.advanced(by: 8).load(as: Int.self))//22: 获取指向后8个字节的指针.取出其中的数据
    ptr.deallocate()//销毁: 释放
    
    ------------------------------------------
    
    var ptr = UnsafeMutablePointer<Int>.allocate(capacity: 3)//创建指针, 分配3*(Int)8=24个空间
    //ptr.initialize(repating: 10, count: 2)//初始化创建8*2=16个字节
    ptr.initialize(to: 11)//初始化前八个字节
    ptr.successor().initialize(to: 22)//.successor()跳8个字节
    ptr.successor().successor().initialize(to: 33)//跳16个字节
    
    print(ptr.pointee) // 11
    // print(ptr.successor().pointee)//22
    print((ptr + 1).pointee) // 22
    // print(ptr.successor().successor().pointee)//33
    print((ptr + 2).pointee) // 33
    /// 等价于上面👆写法:
    print(ptr[0]) // 11
    print(ptr[1]) // 22
    print(ptr[2]) // 33
    
    ptr.deinitalize(count: 3)// 反初始化: 对应初始化
    ptr.deallocate()// 销毁
    
    -----------------------------------------------
    class Person {
      var age: Int
      var name: String
      init(age: Int, name: String) {
        self.age = age
        self.name = name
      }
      deinit{ print(name, "deinit") }
    }
    var ptr = UnsafeMutablePointer<Person>.allocate(capacity: 3)
    ptr.initialize(to: Person(age: 10, name: "jack"))
    (ptr + 1).initialize(to: Person(age: 11, name: "Rose"))
    (ptr + 1).initialize(to: Person(age: 12, name: "Kate"))
    // Jakc deinit
    // Rose deinit
    // Kate deinit
    ptr.deinitialize(count: 3)//反初始化多少个, 销毁多少个
    ptr.deallocate()
    

    3-6、指针之间的转换

    var ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
    
    ptr.assumingMemoryBound(to: Int.self).pointee = 11
    (ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22.0 //非泛型ptr + 8是加8个字节. 如果是泛型ptr + 8是加8*(Int)8=64个字节
    
    print(unsafeBitCase(ptr, to: UnsafePointer<Int>.self).pointee) //11 :将ptr强制转换成UnsafePointer<Int>.self)类型
    print(unsafeBitCase(ptr + 8,to: UnsafePointer<Double>.self).pointee)//22.0
    
    ptr.dellocate()
    
    /*unsafeBitCase是忽略数据类型的强制转换, 不会因为数据类型的变化而改变原来的内存数据
        类似于C++中的reinterpret_cast.
    */
    // 0x00 00 00 00 00 00 00 0A
    var age = 10
    //搜索: 浮点型的存储
    // 0x40 24 00 00 00 00 00 00
    //var age2 = Double(age)//这样转换数据结构会受影响的,会改变内存的存储格式,内存地址指向的8个字节发生变化.即存储在内存中的二进制值是不一样的
    
    // 0x00 00 00 00 00 00 00 0A
    var age1 = 10
    // 0x00 00 00 00 00 00 00 0A
    var age2 = UnsafeBitCast(age1, to: Double.self)
    print(age2)// 5e-323 :科学技术法
    // 内存地址指向的8个字节没有发生变化
    // 不是强制转换成了二进制, 而是直接将二进制数据搬过去,只是类型发生了变化
    

    3-7、补充UnsafeBitCast

    import Foundation
    class Person {}
    var person = Person()//创建一个person对象,它的内存是在堆空间
    
    // person就是指针变量, 只不过编译显示Person类型,不过它其实就是指针
    // 那么我们真想做一个指向堆空间的指针,需要怎么做呢?
    var ptr = withUnsafePointer(to: &person) { UnsafeRawPointer($0) }//代表我们创建了一个指针,由于传入了person变量的地址值, ptr指向是person,并不是堆空间
    var personObjectAddress = ptr.load(as: UInt.self)//取出存储空间的8个字节,即取出来person对象的地址值: 加载它所指向的存储空间,里面放着堆空间Person的地址值
    //创建一个指针指向堆空间.相当于加载一下ptr指针所指向的存储空间里面所存储的东西
    var ptr2 = UnsafeRawPointer(bitPattern: ptr.load(as: UInt.self))//ptr2相当于指向堆空间的一个指针
    print(ptr2)//打印出堆空间的地址值
    
    /* 创建一个指向堆空间地址值指针的 简化版方法: unsafeBitCast( , to: ) */
    var ptr = unsafeBitCast(person, to: UnsafeRawPointer.self)
    print(ptr)//打印出堆空间的地址值
    //相当于ptr指向的就是堆空间的Person()对象
    // 相当于把person变量内存里的数据直接搬到ptr,那么person变量里放着的就是Person()对象堆空间的地址值, 也就是Person()对象堆空间地址值交给ptr去存储, 只不过ptr强制变成指针类型.
    
    var age1: Int = 10
    var age2 = unsafeBitCase(age1, to:Double.self)//相当于将age1内存里的所有数据,也就是age1内存中存储的二进制数据是什么就直接赋值给age2. 相当于age2跟age1内存里的数据是百分百是一样的, 只不过类型不一样, age2变double类型
    
    /*方法二:(略复杂)*/
    var person = Person()
    var address = unsafeBitCase(person, to: UInt.self)//先转换成一个地址值,一个整数(存储的就是堆空间的地址)
    var ptr = UnsafeRawPointer(bitPattern: address)//再把地址值放进去转化成一个指针
    

    第十七章 字面量协议、模式匹配、条件编译

    1-1、字面量(Literal)

    var age = 10
    var isRed = false
    var name = "Jack"
    // 上面代码中的10、false、"Jack"就是字面量
    
    /*
    常见字面量的默认类型
        public typealias IntegerLiteralType = Int //整数类型默认字面量是Int
        public typealias FloatLiteralType = Double //浮点数的默认字面量是Double
        public typealias BooleanLiteralType = Bool 
        public typealias StringLiteralType = String 
    */
    
    //可以通过typelias修饰字面量的默认类型
    typealias FloatLiteralType = Float
    typealias IntegerLiteralType = UInt8
    var age = 10 // UInt8
    var height = 1.68 // Float
    
    /*Swift自带的绝大部分类型, 都支持直接通过字面量进行初始化
        Bool、Int、Float、Double、String、Array、Dictionary、Set、Optional等 
    */
    

    1-2、字面量协议

    /*Swift自带类型之所以能够通过字面量初始化, 是因为他们遵守了对应的协议
        Bool : ExpressibleByBooleanLiteral
        Int : ExpressibleByIntegerLiteral
        Float、Double : ExpressibleByIntegerLiteral、ExpressibleByFloatLiteral
        Dictionary : ExpressibleByDictionaryLiteral
        String : ExpressibleByStringLiteral
        Array、Set : ExpressibleByArrayLiteral
        Optional : ExpressibleByNilLiteral
    */
    var b: Bool = false // ExpressibleByBooleanLiteral
    var i: Int = 10 // ExpressibleByIntegerLiteral
    var f0: Float = 10 // ExpressibleByIntegerLiteral
    var f1: Float = 10.0 // ExpressibleByFloatLiteral
    var d0: Double = 10 // ExpressibleByIntegerLiteral
    var d1: Double = 10.0 // ExpressibleByFloatLiteral
    var s: String = "jack" // ExpressibleByStringLiteral
    var arr: Array = [1, 2, 3] // ExpressibleByArrayLiteral
    var set: Set = [1, 2, 3] // ExpressibleByArrayLiteral
    var dict: Dictionary = ["Jack": 60] // ExpressibleByDictionaryLiteral
    var o: Optional<Int> = nil  // ExpressibleByNilLiteral
    

    1-3、字面量协议应用(一)

    /* 想让某种类型想通过某种字面量来初始化的话, 那么就让某种类型遵守某种字面量协议 */
    // 让Int类型遵守ExpressibleByBooleanLiteral协议, 就可以通过Bool类型字面量初始化Int类型
    extension Int : ExpressibleByBooleanLiteral {
      public init(booleanLiteral value: Bool) { self = value ? 1 : 0 }
    }
    var num: Int = true
    print(num) // 1
    
    /* 有点类似于C++中的转换构造函数 */
    /// 想通过一些字面量,直接初始化你自定义的一些类型, 那么遵守字面量协议就可以了
    class Student : ExpressibleByIntegerLiteral, ExpressibleByFloatLiteral, ExpressibleByStringLiteral, CustomStringConvertible {
        var name: String = ""
      var score: Double = 0
      required init(floatLiteral value: Double) { self.score = value }
      required init(integerLiteral value: Int) { self.score = Double(value) }
      
      required init(StringLiteral value: String) { self.name = value }//普通字符
      required init(unicodeScalarLiteral value: String) { self.name = value }//特殊符号
      required init(extendedGraphemeClusterLiteral value: String) { self.name = value }//特殊符号
      
      var description: String { "name=\(name),score=\(score)" }
    }
    var stu: Student = 90
    print(stu) // name=,score=90.0
    stu = 98.5
    print(stu) // name=,scor=98.5
    stu = "Jack"
    print(stu) // name=Jack,scor=0.0
    

    1-4、字面量协议应用(二)

    struct Point {
      var x = 0.0, y = 0.0
    }
    extension Point : ExpressibleByArrayLiteral, ExpressibleByDictionaryLiteral {
      init(arrayLiteral elements: Double...) {
        guard elements.count > 0 else { return }
        self.x = elements[0]
        guard elements.count > 1 else { return }
        self.y = elements[1]
      }
      init(dictionaryLiteral elements: (String, Double)...) {
        for (k, v) in elements {
          if k == "x" { self.x = v }
          else if k == "y" { self.y = v }
        }
      }
    }
    var p: Point = [10.5, 20.5]
    print(p) // Point(x: 10.5, y: 20.5)
    p = ["x" : 11, "y" : 22]
    print(p) // Point(x: 11.0, y: 22.0)
    

    2-1、模式(Pattern)

    /*
    什么是模式?
        模式是用于匹配的规则, 比如switch的case、捕捉错误的catch、if\guard\while\for语句的条件等.
    
    Swift中的模式有:
        通配符模式 (Wildcard Pattern)
        标识符模式 (Identifier Pattern)
        值绑定模式 (Value-Binding Pattern)
        元组模式 (Tuple Pattern)
        枚举Case模式 (Enumeration Case Pattern)
        可选模式 (Optional Pattern)
        类型转换模式 (Type-Casting Pattern)
        表达式模式 (Expression Pattern)
    */
    

    2-2、通配符模式 (Wildcard Pattern)

    /*
        _  匹配任何值
        _? 匹配非nil值
    */
    enum Life {
        case human(name: String, age: Int?)
      case animal(name: String, age: Int?)
    }
    
    func check(_ life: Life) {
      switch life {
      case .human(let name, _):
        print("human", name)
      case .animal(let name, _?):
        print("animal", name)
      default: 
        print("other")
      }
    }
    check(.human(name: "Rose", age: 20)) // human Rose
    check(.human(name: "jack", age: nil)) // human Jack
    check(.animal(name: "Dog", age: 5)) // animal Dog
    check(.animal(name: "Cat", age: nil)) //other
    

    2-3、标识符模式 (Identifier Pattern)

    //给对应的变量、常量名赋值
    var age = 10
    let name = "jack"
    

    2-4、值绑定模式 (Value-Binding Pattern)

    let point = (3, 2)
    switch point {
    case let (x, y):
      print("The point is at(\(x),\(y)).")
    }
    

    2-5、元组模式 (Tuple Pattern)

    let points = [(0, 0), (1, 0), (2, 0)]
    for (x, _) in points {
      print(x)
    }
    
    let name: String? = "jack"
    let age = 18
    let info: Any = [1, 2]
    switch (name, age, info) {
    case (_?, _, _ as String):
      print("case")
    defalut:
      print("default")
    }// default
    
    var scores = ["jack" : 98, "rose" : 100, "kate": 86]
    for (name, score) in scores {
      print(name, score)
    }
    

    2-6、枚举Case模式 (Enumeration Case Pattern)

    /* if case语句等价于只有1个case的switch语句 */
    let age = 2
    // 原来的写法
    if age >= 0 && age <= 9 {
      print("[0, 9]")
    }
    // 枚举用例模式
    if case 0...9 = age {//判断age是否在0..9区间内
      print("[0, 9]")
    }
    guard case 0...9 = age else { return }
    print("[0, 9]")
    
    
    switch age {
    case 0...9: print("[0, 9]")
    default: break
    }
    
    let ages: [Int?] = [2, 3, nil, 5]
    for case nil in ages {//拿出数组ages里的每一个值,与case进行匹配,如果有nil就执行里面的语句
      print("有nil值")
      break
    }// 有nil值
    
    
    let points = [(1, 0), (2, 1), (3, 0)]
    for case let (x, 0) in points {//匹配出points里面y为0的元素
      print(x)
    }// 1 3
    

    2-7、 可选模式 (Optional Pattern)

    let age: Int? = 42
    if case .some(let x) = age { print(x) }
    if case let x? = age { print(x) }
    
    let ages: [Int?] = [nil, 2, 3, nil, 5]
    for case let age? in ages {//模式匹配: 非空则执行里面
      print(age)
    }// 2 3 5
    /// 跟上面👆的for, 效果是等价的
    let ages: [Int?] = [nil, 2, 3, nil, 5]
    for item in ages {//这样写不简洁, 不如上面的匹配模式
      if let age = item {
        print(age)
      }
    } 
    
    func check(_ num: Int?) {
      switch num {
      case 2?: print("2")//非空, 传2时执行
      case 4?: print("4")
      case 6?: print("6")
      case _?: print("other")//只要为非空, 都可以
      case _: print("nil")//不管是空还是非空,都可以
      }
    }
    check(4) // 4
    check(8) // other
    check(nil) // nil
    
    
    /* 回忆补充: */
    var age: Int? = 10
    switch age {
    case let x?:
      print(x)
    case nil:
      print("nil")
    }
    //等价于👆
    switch age {
    case .some(let x):
      print(x)
    case .none:
      print("nil")
    }
    

    2-8、类型转换模式 (Type-Casting Pattern)

    let num: Any = 6
    switch num {
    case is Int:
        // 编译器依然认为num是Any类型
      print("is Int", num)
    // case let n as Int://把n强转为int类型
    // print("as Int", n + 1)
    default:
      break
    } 
    
    
    class Animal { func eat() { print(type(of: self), "eat") } }
    class Dog : Animal { func run() { print(type(of: self). "run") } }
    class Cat : Animal { func jump() { print(type(of: self), "jump") } }
    func check(_ animal: Animal) {
      switch animal {
      case let dog as Dog://判断能不能强转: 把dog强转为Dog类型, 不会对animal造成影响
        dog.eat()
        dog.run()
      case is Cat:// 判断是不是Cat类型
        animal.eat()
      default: break
      }
    }
    // Dog eat
    // Dog run
    check(Dog())
    // Cat eat
    check(Cat())
    

    2-9、表达式模式 (Expression Pattern)

    /* 表达式模式用在case中 */
    let point = (1, 2)//元组
    switch point {
    case (0, 0):
      print("(0, 0) is at the origin.")
    case (-2...2, -2...2):
      print("(\(point.0), \(point.1)) is near the origin.")
    default:
      print("The point is at(\(point.0), \(point.1)).")
    }// (1, 2) is near the origin.
    
    
    /* 自定义表达式模式 一 */
    /// 可以通过重载运算符, 自定义表达式模式的匹配规则
    struct Struct {
        var score = 0, name = ""
      // pattern: case后面的内容, value: switch后面的内容
      static func ~= (pattern: Int, value: Student) -> Bool { value.score >= pattern }
      static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool { pattern.contains(value.score) }
      static func ~= (pattern: Range<Int>, value: Student) -> Bool { pattern.contains(value.score) }
    }
    
    var stu = Student(score: 75, name: "Jack")
    switch stu {// 通过自定义表达式, 实现stu与范围值进行匹配
    case 100: print(">= 100")
    case 90: print(">= 90")
    case 80..<90: print("[80, 90)")
    case 60...79: print("[60, 70]")
    case 0: print(">= 0")
    default: break
    }// [60, 79]
    
    if case 60 = stu {
      print(">=60")
    }// >=60
    
    var info = (Student(score: 70, name: "Jack"), "及格")
    switch info {
    case let (60, text): print(text)
    default: break
    }// 及格
    
    /* 自定义表达式模式 二 */
    extension String {
        static func ~= (pattern: (String) -> Bool, value: String) -> Bool {
        pattern(value)
      }
    }
    
    func hasPrefix(_ prefix: String) -> ((String) -> Bool) { { $0.hasPrefix(prefix) } }
    func hasSuffix(_ suffix: String) -> ((String) -> Bool) { { $0.hasSuffix(suffix) } }
    
    var str = "jack"
    switch str {
    case hasPrefix("j"), hasSuffix("k"): // (String) -> Bool
      print("以j开头, 以k结尾")
    default: break
    }// 以j开头, 以k结尾
    
    
    /* 自定义表达式模式 三 */
    func isEven(_ i: Int) -> Bool { i % 2 == 0 }
    func isOdd(_i: Int) -> Bool { i % 2 != 0 }
    
    extension Int {
      static func ~= (pattern: (Int) -> Bool, value: Int) -> Bool {
            pattern(value)   
      }
    }
    
    var age = 9
    switch age {
    case isEvent://case后面放的是函数类型, 不是在调用函数
      print(age, "是个偶数")
    case isOdd:
      print(age, "是个奇数")
    default: break
    }
    
    
    /* 自定义表达式模式 四 */
    prefix operator ~>
    prefix operator ~>=
    prefix operator ~<
    prefix operator ~<=
    prefix func ~> (_ i: Int) -> ((Int) -> Bool) { { $0 > i } }
    prefix func ~>= (_ i: Int) -> ((Int) -> Bool) { { $0 >= i } }
    prefix func ~< (_ i: Int) -> ((Int) -> Bool) { { $0 < i } }
    prefix func ~<= (_ i: Int) -> ((Int) -> Bool) { { $0 <= i } }
    
    var age = 9
    switch age {
    case ~>=0:
    print("1")
    case ~>10:
    print("2")
    default: break
    } // [0, 10]
    

    2-10、where

    /* 可以使用where为模式匹配增加匹配条件 */
    var data = (10, "Jack")
    switch data {
    case let (age, _) where age > 10:
    print(data.1, "age>10")
    case let (age, _) where age > 0:
    print(data.1, "age>0")
    default: break
    }
    
    var ages = [10, 20, 44, 23, 55]
    for age in ages where age > 30 {
    print(age)
    } // 44 55
    
    protocol Stackable { associatedtype Element }
    protocol Container {
    associatedtype Stack : Stackable where Stack.Element : Equatable
    }
    
    func equal<S1: Stackable, S2: Stackable>(_ s1: S1, _ s2: S2) -> Bool
    where S1.Element == S2.Element, S1.Element : Hashable {
    return false
    }
    
    extension Container where Self.Stack.Element : Hashable { }
    

    3-1、MARK、TODO、FIXME

    // MARK: 类似于OC中的 #pragma mark
    // MARK: - 类似于OC中的 #pragma mark -
    // TODO: 用于标记未完成的任务
    // FIXME: 用于标记待修复的问题
    
    #warning("undo") //这个比较明显
    

    3-2、条件编译

    // 操作系统:macOS\iOS\tvOS\watchOS\Linux\Android\Windows\FreeBSD
    #if os(macOS) || os(iOS)
    // CPU架构:i386\x86_64\arm\arm64
    #elseif arch(x86_64) || arch(arm64)
    // swift版本
    #elseif swift(<5) && swift(>=3)
    // 模拟器
    #elseif targetEnvironment(simulator)
    // 可以导入某模块
    #elseif canImport(Foundation)
    #else
    #endif
    
    
    // debug模式
    #if DEBUG
    
    // release模式
    #else
    
    #endif
    
    /* 可自定义: 需要在BuildSetting-->Swift Compiler - Custom Flags--> Active Compilation Conditions --> Debug 中添加例如"Test"就可以使用下面 */
    #if TEST
    print("test")
    #endif
    #if OTHER
    print("other")
    #endif
    

    3-3、打印

    func log<T>(_ msg: T,
                file: NSString = #file, //当前的函数环境所在文件(这里调用时,通过实参传递过来)
                line: Int = #line, //当前的函数环境所在行数
                fn: String = #function) { //当前的函数环境所在函数
        #if DEBUG
        let prefix = "\(file.lastPathComponent)_\(line)_\(fn):"
        print(prefix, msg)
        #endif
    }
    

    3-4、系统版本检测

    if #available(iOS 10, macOS 10.12, *) {
      // 对于iOS平台,只在iOS10及以上版本执行
      // 对于macOS平台,只在macOS 10.12及以上版本执行
      // 最后的*表示在其他所有平台都执行
    }
    

    3-5、API可用性说明

    @available(iOS 10, macOS 10.15, *)
    class Person {}
    
    struct Student {
      @available(*, unavailable, renamed: "study")//表明下面方法的方法名改变了
      func study_() {}
      func study() {}
      
      @available(iOS, deprecated: 11)//会提示下面的方法在iOS 11过期了
      @available(macOS, deprecated: 10.12)//会提示下面的方法在macOS 10.12过期了
      func run() {}
    }
    /* 更多用法参考 : https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
     */
    
    var stu =  Student()
    stu.run()//
    

    第十八章 从OC到Swift

    1-1、补充

    /*
    1. catch { error } // catch里面自带error
    2. #warning("undo")
    3. 编码习惯
    */
    
    /// 写一个方法的时候, 一时半会不知道怎么写, 又不想让它报错, 可以用fatalError()占位
    func test() -> Person {
        #warning("undo")
      fatalError()
    }
    

    2-1、iOS程序的入口

    /*
    在AppDelegate上面默认有个@UIApplicationMain标记,这表示
        编译器自动生成入口代码(main函数代码),自动设置AppDelegate为APP的代理
    
    也可以删掉@UIApplicationMain,自定义入口代码:新建一个main.swift文件
    */
    import UIKit 
    class MJApplication : UIApplication {}
    UIApplicationMain(CommandLine.argc,
                     CommandLine.unsafeArgv,
                     NSStringFromClass(MJApplication.self),
                     NSStringFromClass(AppDelegate.self))
    

    3-1、Swift调用OC

    /*
    新建1个桥接头文件,文件名格式默认为:{targetName}-Bridging-Header.h
            在Build Settings --> Objective-C Bridging Header -->写入桥接文件地址
    
    在 {targetName}-Bridging-Header.h 文件中 #import OC需要暴露给Swift的内容
        #import "MJPerson.h"
    */
    
    /// Swift调用OC – MJPerson.h
    int sum(int a, int b);
    
    @interface MJPerson : NSObject
    @property (nonatomic, assign) NSInteger age;
    @property (nonatomic, copy) NSString *name;
    
    - (instancetype)initWithAge:(NSInteger)age name:(NSString *)name;
    + (instancetype)personWithAge:(NSInteger)age name:(NSString *)name;
    
    - (void)run;
    + (void)run;
    
    - (void)eat:(NSString *)food other:(NSString *)other;
    + (void)eat:(NSString *)food other:(NSString *)other;
    @end
    
    /// Swift调用OC – MJPerson.m
    @implementation MJPerson
    - (instancetype)initWithAge:(NSInteger)age name:(NSString *)name {
        if (self = [super init]) {
            self.age = age;
            self.name = name;
        }
        return self;
    }
    + (instancetype)personWithAge:(NSInteger)age name:(NSString *)name {
        return [[self alloc] initWithAge:age name:name];
    }
    
    + (void)run { NSLog(@"Person +run"); }
    - (void)run { NSLog(@"%zd %@ -run", _age, _name); }
    
    + (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"Person +eat %@ %@", food, other); }
    - (void)eat:(NSString *)food other:(NSString *)other { NSLog(@"%zd %@ -eat %@ %@", _age, _name, food, other); }
    @end
    
    int sum(int a, int b) { return a + b; }
    
    /// Swift调用OC – Swift代码
    var p = MJPerson(age: 10, name: "Jack")
    p.age = 18
    p.name = "Rose"
    p.run() // 18 Rose -run
    p.eat("Apple", other: "Water") // 18 Rose -eat Apple Water
    
    MJPerson.run() // Person +run
    MJPerson.eat("Pizza", other: "Banana") // Person +eat Pizza Banana
    
    print(sum(10, 20)) // 30 
    
    /*
    如果C语言暴露给Swift的函数名跟Swift中的其他函数名冲突了
        可以在Swift中使用 @_silgen_name 修改C函数名
    */
    // C语言
    int sum(int a, int b) {
            return a + b;
    }
    // Swift
    @_silgen_name("sum") func swift_sum(_ v1: Int32, _ v2: Int32) -> Int32 //给sum进行重命名swift_sum
    print(swift_sum(10, 20)) // 30
    print(sum(10, 20)) // 30
    

    3-2、OC调用Swift

    /*
    Xcode已经默认生成一个用于OC调用Swift的头文件,文件名格式是: {targetName}-Swift.h
    
    //在下面的路径可以看到
         在Build Settings --> Swift Compiler - General --> Objective-C Generated Interface Header Name --> xxxSwift.h
    */
    import Foundation
    
    @objcMembers class Car: NSObject {
      var price: Double
      var band: String
      init(price: Double, band: String) {
        self.price = price
        self.band = band
      }
      func run() { print(price, band, "run") }
      static func run() { print("Car run") }
    }
    
    extension Car {
      func test() { print(price, band, "test") }
    }
    /*
    Swift暴露给OC的类最终继承自NSObject
    
    使用@objc修饰需要暴露给OC的成员
    
    使用@objcMembers修饰类
            代表默认所有成员都会暴露给OC(包括扩展中定义的成员)
            最终是否成功暴露,还需要考虑成员自身的访问级别
    */
    
    /// OC调用Swift – {targetName}-Swift.h
    // Xcode会根据Swift代码生成对应的OC声明,写入 {targetName}-Swift.h 文件
    
    #import "TestSwift-Swift.h"
    @interface Car : NSObject
    @property (nonatomic) double price;
    @property (nonatomic, copy) NSString * _Nonnull band;
    - (nonnull instancetype)initWithPrice:(double)price band:(NSString * _Nonnull)band OBJC_DESIGNATED_INITIALIZER;
    - (void)run;
    + (void)run;
    - (nonnull instancetype)init SWIFT_UNAVAILABLE;
    + (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
    @end
    
    @interface Car (SWIFT_EXTENSION(备课_Swift))
    - (void)test;
    @end
    
    /// OC调用Swift – OC代码
    #import "备课_Swift-Swift.h"
    int sum(int a, int b) {
        Car *c = [[Car alloc] initWithPrice:10.5 band:@"BMW"];
        c.band = @"Bently";
        c.price = 108.5;
        [c run]; // 108.5 Bently run
        [c test]; // 108.5 Bently test
        [Car run]; // Car run
        return a + b;
    }
    
    /// OC调用Swift – @objc
    // 可以通过 @objc 重命名Swift暴露给OC的符号名(类名、属性名、函数名等)
    @objc(MJCar)//暴露的同时进行重命名,比如此处将Car暴露成MJCar
    @objcMembers class Car: NSObject {//此处必须继承自NSObject; @objcMembers 表示所有的成员都暴露给OC
        var price: Double
        @objc(name)//@objc 表示部分成员暴露给OC. 把band重命名为name
        var band: String
        init(price: Double, band: String) {
            self.price = price
            self.band = band
        }
        @objc(drive)
        func run() { print(price, band, "run") }
        static func run() { print("Car run") }
    }
    extension Car {
        @objc(exec:v2:)
        func test() { print(price, band, "test") }
    }
    
    MJCar *c = [[MJCar alloc] initWithPrice:10.5 band:@"BMW"];
    c.name = @"Bently";
    c.price = 108.5;
    [c drive]; // 108.5 Bently run
    [c exec:10 v2:20]; // 108.5 Bently test
    [MJCar run]; // Car run
    

    4-1、选择器(Selector)

    /*Swift中依然可以使用选择器,使用#selector(name)定义一个选择器
      必须是被@objcMembers或@objc修饰的方法才可以定义选择器
    */
    // Swift类要暴露给OC使用的话, 必须继承自NSObjct
    @objcMembers class Person: NSObject {
        func test1(v1: Int) { print("test1") }
        func test2(v1: Int, v2: Int) { print("test2(v1:v2:)") }
        func test2(_ v1: Double, _ v2: Double) { print("test2(_:_:)") }
        func run() {
            perform(#selector(test1))//执行方法
            perform(#selector(test1(v1:)))
            perform(#selector(test2(v1:v2:)))
            perform(#selector(test2(_:_:)))
            perform(#selector(test2 as (Double, Double) -> Void))
        }
    }
    /*
        1.为什么Swift暴露给OC的类最终要继承自NSObject?
            因为OC依赖于Runtime,Runtime要求类是有isa指针,isa指针必须继承自NSObject才有.
    
        2.oc里的方法拿到Swift里面调用, 底层是怎么调用的? 反过来, OC调用Swift底层又是如何调用?
        Swift调用OC写的方法, 走的是Runtime流程.
        OC调用Swift类, 走的也是Runtime流程
        
        3.如果Swift中给OC暴露了方法, 但是在Swift中调用了该方法, 那么底层是怎么调用的?
        是按照Swift的方式去调用的,没有必要走msg_send消息机制.从性能上讲比Runtime的性能要快一些, 因为Runtime那一套还要通过isa指针一层一层去找方法.
    
    备注: 如果希望Swift中的方法必须走OC的Runtime机制的话, 需要在方法前加上 dynamic关键字.
    */
    

    5-1、String

    /// Swift的字符串类型String,跟OC的NSString,在API设计上还是有较大差异
    
    // 空字符串
    var emptyStr1 = ""
    var emptyStr2 = String()
    
    var str: String = "1"
    // 拼接,jack_rose
    str.append("_2")
    // 重载运算符 +
    str = str + "_3"
    // 重载运算符 +=
    str += "_4"
    // \()插值
    str = "\(str)_5"
    // 长度,9,1_2_3_4_5
    print(str.count)
    
    var str = "123456"
    print(str.hasPrefix("123")) // true
    print(str.hasSuffix("456")) // true
    

    5-2、String的插入和删除

    var str = "1_2"
    // 1_2_
    str.insert("_", at: str.endIndex)
    // 1_2_3_4
    str.insert(contentsOf: "3_4", at: str.endIndex)
    // 1666_2_3_4
    str.insert(contentsOf: "666", at: str.index(after: str.startIndex))
    // 1666_2_3_8884
    str.insert(contentsOf: "888", at: str.index(before: str.endIndex))
    // 1666hello_2_3_8884
    str.insert(contentsOf: "hello", at: str.index(str.startIndex, offsetBy: 4))
    
    // 666hello_2_3_8884
    str.remove(at: str.firstIndex(of: "1")!)
    // hello_2_3_8884
    str.removeAll { $0 == "6" }//发现字符是6的就删掉
    var range = str.index(str.endIndex, offsetBy: -4)..<str.index(before: str.endIndex)
    // hello_2_3_4
    str.removeSubrange(range)//按范围删除
    

    5-3、Substring

    /* 
        String可以通过下标、 prefix、 suffix等截取子串,子串类型不是String,而是Substring 
    */
    
    var str = "1_2_3_4_5"
    // 1_2
    var substr1 = str.prefix(3) 
    // 4_5
    var substr2 = str.suffix(3)//substr2作为指针指向同一块内存的指定区域
    // 1_2
    var range = str.startIndex..<str.index(str.startIndex, offsetBy: 3)
    var substr3 = str[range] //Substring类型
    
    // 最初的String,1_2_3_4_5
    print(substr3.base)// 指向原来的字符串
    
    // Substring -> String
    var str2 = String(substr3)
    
    /*
     Substring和它的base,共享字符串数据
     Substring发生修改 或者 转为String时,会分配新的内存存储字符串数据
    */
    
    var str = "123456"
    var substr = str[str.startIndex..<str.index(str.startIndex, offsetBy: 2)]
    substr.append(contensOf: "666")
    print(str, substr)// 123456 12666
    

    5-4、String 与 Character

    for c in "jack" { // c是Character类型
        print(c)
    }
    
    var str = "jack"
    // c是Character类型
    var c = str[str.startIndex]
    

    5-5、String相关的协议

    /* 
    BidirectionalCollection 协议包含的部分内容
        startIndex 、 endIndex 属性、index 方法
        String、Array 都遵守了这个协议
    
    RangeReplaceableCollection 协议包含的部分内容
        append、insert、remove 方法
        String、Array 都遵守了这个协议
    
    Dictionary、Set 也有实现上述协议中声明的一些方法,只是并没有遵守上述协议
    */
    

    5-6、多行String

    let str = """
    1
            "2"
    3
            '4'
    """
    
    
    // 如果要显示3引号,至少转义1个引号
    let str = """
    Escaping the first quote \"""
    Escaping two quotes \"\""
    Escaping all three quotes \"\"\"
    """
    
    // 缩进以结尾的3引号为对齐线
    let str = """
                    1
                            2
            3
                    4
            """
    
    
    // 以下2个字符串是等价的
    let str1 = "These are the same."
    let str2 = """
    These are the same.
    """
    

    5-7、String 与 NSString

    /*
    String 与 NSString 之间可以随时随地桥接转换
         如果你觉得String的API过于复杂难用,可以考虑将String转为NSString
    */
    var str1: String = "jack"
    var str2: NSString = "rose"
    
    var str3 = str1 as NSString
    var str4 = str2 as String
    
    // ja
    var str5 = str3.substring(with: NSRange(location: 0, length: 2))
    print(str5)
    
    /*
    比较字符串内容是否等价
      String使用 == 运算符
      NSString使用isEqual方法,也可以使用 == 运算符(本质还是调用了isEqual方法)
    */
    

    6-1、Swift、OC桥接转换表

    String ⇌ NSString
    String ← NSMutableString
    Array ⇌ NSArray
    Array ← NSMutableArray
    Dictionary ⇌ NSDictionary
    Dictionary ← NSMutableDictionary
    Set ⇌ NSSet
    Set ← NSMutableSet
    
    //👆上面显示的是能否通过as进行转换
    

    第十九章 从OC到Swift、函数式编程

    1-1、只能被class继承的协议

    protocol Runnable1: AnyObject {}//方式一
    protocol Runnable2: class {}//方式二
    @objc protocol Runnable3 {}//方式三
    /* 被 @objc 修饰的协议,还可以暴露给OC去遵守实现 */
    

    1-2、可选协议

    /* 可以通过 @objc 定义可选协议,这种协议只能被 class 遵守 */
    @objc protocol Runnable {
        func run1()
        @objc optional func run2()// 这样也可以实现可选协议
        func run3()
    }
    
    class Dog: Runnable {
        func run3() { print("Dog run3") }
        func run1() { print("Dog run1") }
    }
    var d = Dog()
    d.run1() // Dog run1
    d.run3() // Dog run3
    

    1-3、dynamic

    /* 被 @objc dynamic 修饰的内容会具有动态性,比如调用方法会走runtime那一套流程 */
    class Dog: NSObject {
        @objc dynamic func test1() {}//走runtime那一套流程
        func test2() {}//走虚表那一套流程
    }
    var d = Dog()
    d.test1()
    d.test2()
    

    1-4、KVC\KVO

    /*Swift 支持 KVC \ KVO 的条件:
        属性所在的类、监听器最终继承自 NSObject
        用 @objc dynamic 修饰对应的属性
    */
    class Observer: NSObject {
        override func observeValue(forKeyPath keyPath: String?,
                                    of object: Any?,
                                    change: [NSKeyValueChangeKey : Any]?,
                                    context: UnsafeMutableRawPointer?) {
             print("observeValue", change?[.newKey] as Any)
        }
    }
    
    class Person: NSObject {
        @objc dynamic var age: Int = 0
        var observer: Observer = Observer()
        override init() {
            super.init()
            self.addObserver(observer,
                            forKeyPath: "age",
                            options: .new,
                            context: nil)
        }
        deinit {
            self.removeObserver(observer,
                                forKeyPath: "age")
        }
    }
    var p = Person()
    // observeValue Optional(20)
    p.age = 20
    // observeValue Optional(25)
    p.setValue(25, forKey: "age")
    

    1-5、block方式的KVO

    class Person: NSObject {
        @objc dynamic var age: Int = 0
        var observation: NSKeyValueObservation?
        override init() {
            super.init()
            observation = observe(\Person.age, options: .new) {
                (person, change) in
                print(change.newValue as Any)
            }
        }
    }
    var p = Person()
    // Optional(20)
    p.age = 20
    // Optional(25)
    p.setValue(25, forKey: "age")
    
    

    2-1、关联对象(Associated Object)

    /*
    在Swift中,class依然可以使用关联对象. 结构体和枚举不可以使用关联对象.
    
    默认情况,extension不可以增加存储属性
         借助关联对象,可以实现类似extension为class增加存储属性的效果
    */
    class Person {}
    extension Person {
        private static var AGE_KEY: Void?// key名字随便取,只需保证唯一性. 类型属性只能通过类名去调用,所以要用大写的Self. 这个也是占一个字节.
        private static var WEIGHT_KEY = false//这个只是拿来用它们的地址值而已, 所以能省内存尽量省内存, 选用的bool类型, 它只占1个字节.
      
        var age: Int {
            get {
                (objc_getAssociatedObject(self, &Self.AGE_KEY) as? Int) ?? 0
            }
            set {
                objc_setAssociatedObject(self,//对象本身
                                        &Self.AGE_KEY,//地址值, 用来取出对应的newValue值
                                        newValue,
                                        .OBJC_ASSOCIATION_ASSIGN)
            }
        }
        var weight: Int {
            get {
                (objc_getAssociatedObject(self, &Self.WEIGHT_KEY) as? Int) ?? 0
            }
            set {
                objc_setAssociatedObject(self,
                                        &Self.WEIGHT_KEY,
                                        newValue,
                                        .OBJC_ASSOCIATION_ASSIGN)
            }
        }
    }
    
    var p = Person()
    print(p.age) // 0
    p.age = 10
    print(p.age) // 10
    

    3-1、资源名管理

    let img = UIImage(named: "logo")
    
    let btn = UIButton(type: .custom)
    btn.setTitle("添加", for: .normal)
    
    performSegue(withIdentifier: "login_main", sender: self)
    
    
    /*这种做法实际上是参考了Android的资源名管理方式*/
    extension UIImage {
        convenience init?(_ name: R.image) {
        self.init(named: name.rawValue)
        }
    }
    extension UIViewController {
        func performSegue(withIdentifier identifier: R.segue, sender: Any?) {
                performSegue(withIdentifier: identifier.rawValue, sender: sender)
        }
    }
    extension UIButton {
        func setTitle(_ title: R.string, for state: UIControl.State) {
                setTitle(title.rawValue, for: state)
        }
    }
    
    
    enum R {
        enum string: String {
                case add = "添加"
        }
        enum image: String {
                case logo
        }
        enum segue: String {
                case login_main
        }
    }
    
    let img = UIImage(R.image.logo)
    
    let btn = UIButton(type: .custom)
    btn.setTitle(R.string.add, for: .normal)
    
    performSegue(withIdentifier: R.segue.login_main, sender: self)
    

    3-2、资源名管理的其他思路

    let img = UIImage(named: "logo")
    let font = UIFont(name: "Arial", size: 14)
    
    
    enum R {
        enum image {
                static var logo = UIImage(named: "logo")//静态属性
        }
        enum font {
            static func arial(_ size: CGFloat) -> UIFont? {
                UIFont(name: "Arial", size: size)
            }
        }
    }
    let img = R.image.logo
    let font = R.font.arial(14)
    
    /*更多优秀的思路参考:
    https://github.com/mac-cain13/R.swift
    https://github.com/SwiftGen/SwiftGen
    */
    

    4-1、多线程开发 – 异步

    /* //gcd
    print(Thread.current, "main")
    DispatchQueue.global().async {
        print(Thread.current, "async")
            DispatchQueue.main.async {
                print(Thread.current, "main")
        }
    }
    */
    
    /// 封装gcd
    public typealias Task = () -> Void
    
    public struct Asyncs {
        public static func async(_ task: @escaping Task) {
            _async(task)
        }
        public static func async(_ task: @escaping Task,
                                _ mainTask: @escaping Task) {
            _async(task, mainTask)
        }
        private static func _async(_ task: @escaping Task,
                                  _ mainTask: Task? = nil) {
            let item = DispatchWorkItem(block: task)
            DispatchQueue.global().async(execute: item)
            if let main = mainTask {
                item.notify(queue: DispatchQueue.main, execute: main)
            }
        }
    }
    
    /// 使用:
    Asyncs.async {
      print(1)//子线程
    }
    
    Asyncs.async({
      print(1, Thread.current)//子线程
    }) {
        print(2, Thread.current)//主线程
    }
    

    4-2、多线程开发 – 延迟

    /// 封装
    public struct Asyncs {
        @discardableResult //这个关键字表示可以忽略下面方法的返回值.就是没有用到它的返回值也不会收到警告
        public static func delay(_ seconds: Double,
                                _ block: @escaping Task) -> DispatchWorkItem {
            let item = DispatchWorkItem(block: block)
            // 主线程延迟几秒执行
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds,
                                          execute: item)
            // 子线程延迟几秒执行
            //DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
            //                                                          execute: item)
            return item
        }
    }
    
    /// 使用:
    private var item: DispatchWorkItem?
    
    item = Asyncs.asyncDelay(3.0, { //定义任务
      print(1)
    }){
      print(2)
    }
    
    item?.cancel()//可以随时取消任务
    

    4-3、多线程开发 – 异步延迟

    /// 封装
    public struct Asyncs {
        @discardableResult
        public static func asyncDelay(_ seconds: Double,
                                      _ task: @escaping Task) -> DispatchWorkItem {
            return _asyncDelay(seconds, task)
        }
        @discardableResult
        public static func asyncDelay(_ seconds: Double,
                                      _ task: @escaping Task,
                                      _ mainTask: @escaping Task) -> DispatchWorkItem {
            return _asyncDelay(seconds, task, mainTask)
        }
        private static func _asyncDelay(_ seconds: Double,
                                        _ task: @escaping Task,
                                        _ mainTask: Task? = nil) -> DispatchWorkItem {
            let item = DispatchWorkItem(block: task)
            DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + seconds,
                                              execute: item)
            if let main = mainTask {
                item.notify(queue: DispatchQueue.main, execute: main)
            }
            return item
        }
    }
    

    4-4、多线程开发 – once

    /*
     dispatch_once在Swift中已被废弃,取而代之
            可以用类型属性或者全局变量\常量
            默认自带 lazy + dispatch_once 效果
    */
    /// 方法二: 只在文件中执行
    fileprivate let initTask2: Void = {// 全局变量
            print("initTask2---------")
    }()
    
    class ViewController: UIViewController {
        // 方法一: 整个运行过程中只会初始化一次, 这里面的代码也只会执行一次
        // 它本质是全局变量,只初始化一次, 而且是lazy的, 用到的时候才初始化 
        static let initTask1: Void = {
                print("initTask1---------")
        }()
        override func viewDidLoad() {
            super.viewDidLoad()
            let _ = Self.initTask1
            let _ = initTask2
        }
    }
    

    4-5、多线程开发 – 加锁

    /* gcd信号量 */
    /// 比如做一个全局缓存类
    class Cache {
        private static var data = [String: Any]()//全局变量
      
        private static var lock = DispatchSemaphore(value: 1)//允许同时最多只有1条线程访问它
        static func set(_ key: String, _ value: Any) {
                lock.wait()//加锁
                defer { lock.signal() } //解锁
          
                data[key] = value
        }
    }
    
    /* Foundation */
    private static var lock = NSLock()
    static func set(_ key: String, _ value: Any) {
            lock.lock()
            defer { lock.unlock() }
    }
    
    private static var lock = NSRecursiveLock()
    static func set(_ key: String, _ value: Any) {
            lock.lock()
            defer { lock.unlock() }
    }
    

    5-1、Array的常见操作

    var arr = [1, 2, 3, 4]
    
    // [2, 4, 6, 8]
    var arr2 = arr.map { $0 * 2 }
    
    // [2, 4]
    var arr3 = arr.filter { $0 % 2 == 0 }
    
    // 10
    // $0 上一次遍历返回的结果(初始值是0)
    // $1 每次遍历到的数组元素
    var arr4 = arr.reduce(0) { $0 + $1 }//设置初始值为0
    
    // 10
    var arr5 = arr.reduce(0, +)
    
    
    func double(_ i: Int) -> Int { i * 2 } 
    var arr = [1, 2, 3, 4]
    // [2, 4, 6, 8]
    print(arr.map(double))
    
    
    /* 
    var arr = Array.init(repeating: 4, count: 5)
    print(arr) //[4, 4, 4, 4, 4]
    */
    var arr = [1, 2, 3]
    // [[1], [2, 2], [3, 3, 3]]
    var arr2 = arr.map { Array.init(repeating: $0, count: $0) }
    // [1, 2, 2, 3, 3, 3]
    var arr3 = arr.flatMap { Array.init(repeating: $0, count: $0) }
    /*flatMap会将里面的元素平铺出来,要求返回数组*/
    
    
    var arr = ["123", "test", "jack", "-30"]
    // [Optional(123), nil, nil, Optional(-30)]
    var arr2 = arr.map { Int($0) }
    // [123, -30]
    func double(_ i: Int) -> Int { i * 2 } var arr3 = arr.compactMap { Int($0) }
    
    
    // 使用reduce实现map、filter的功能
    var arr = [1, 2, 3, 4]
    // [2, 4, 6, 8]
    print(arr.map { $0 * 2 })
    print(arr.reduce([]) { $0 + [$1 * 2] })
    // [2, 4]
    print(arr.filter { $0 % 2 == 0 })
    print(arr.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 })
    

    5-2、lazy的优化

    let arr = [1, 2, 3]
    let result = arr.lazy.map {
        (i: Int) -> Int in
        print("mapping \(i)")
        return i * 2
    }
    print("begin-----")
    print("mapped", result[0])//用到这个的时候才会映射
    print("mapped", result[1])
    print("mapped", result[2])
    print("end----")
    /*打印结果:
    begin-----
    mapping 1
    mapped 2
    mapping 2
    mapped 4
    mapping 3
    mapped 6
    end----
    */
    

    5-3、可选类型Optional的map和flatMap

    var num1: Int? = 10
    // Optional(20)
    var num2 = num1.map { $0 * 2 }//如果num1有值,会将num1先解包传入进行操作, map会将值再包装一层返回出去
    var num3: Int? = nil
    // nil
    var num4 = num3.map { $0 * 2 }//有值才会执行里面代码, 否则直接返回nil
    
    
    var num1: Int? = 10
    // Optional(Optional(20))
    var num2 = num1.map { Optional.some($0 * 2) }
    // Optional(20)
    var num3 = num1.flatMap { Optional.some($0 * 2) }//如果发现你里面本身就是可选类型,flatMap就不会再包装一层
    
    
    var num1: Int? = 10
    var num2 = (num1 != nil) ? (num1! + 10) : nil
    var num3 = num1.map { $0 + 10 }
    // num2、num3是等价的
    
    
    var fmt = DateFormatter()
    fmt.dateFormat = "yyyy-MM-dd"
    var str: String? = "2011-09-10"
    // old
    var date1 = str != nil ? fmt.date(from: str!) : nil
    // new
    // var date1 = str.flatMap { fmt.date(from:$0) }//flatMap是传入一个函数,如果str有值的话, 它会将str的值传给这个函数, 现在由于这个函数是个闭包表达式, 所以传给函数的东西变成了$0,再执行函数,返回date
    var date2 = str.flatMap(fmt.date)//现在直接将fmt.date这个函数传进去,那就代表会将str进行解包,解包之后直接传给fmt.date
    
    
    var score: Int? = 98
    // old
    var str1 = score != nil ? "socre is \(score!)" : "No score"
    // new
    var str2 = score.map { "score is \($0)" } ?? "No score"
    
    
    /* 根据person的名称找到person实例 */
    struct Person {
            var name: String
            var age: Int
    }
    var items = [
            Person(name: "jack", age: 20),
            Person(name: "rose", age: 21),
            Person(name: "kate", age: 22)
    ]
    /*查找数组索引:
      var items = [11, 22, 33]
      let index = items.firstIndex { $0 == 22 }
      print(index)
    */
    // old
    func getPerson1(_ name: String) -> Person? {
            let index = items.firstIndex { $0.name == name }
            return index != nil ? items[index!] : nil
    }
    // new
    func getPerson2(_ name: String) -> Person? {
            return items.firstIndex { $0.name == name }.map { items[$0] }
    }
    
    
    /*字典转Person模型*/
    struct Person {
        var name: String
        var age: Int
        init?(_ json: [String : Any]) {
            guard let name = json["name"] as? String,
                        let age = json["age"] as? Int else {
                    return nil
            }
            self.name = name
            self.age = age
        }
    }
    var json: Dictionary? = ["name" : "Jack", "age" : 10]
    // old
    var p1 = json != nil ? Person(json!) : nil
    // new
    var p2 = json.flatMap(Person.init)
    

    相关文章

      网友评论

          本文标题:Swift语法知识汇总(下)

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