美文网首页Swift学习
Swift语法知识汇总(上)

Swift语法知识汇总(上)

作者: Amok校长 | 来源:发表于2020-06-12 09:33 被阅读0次

    导语

    历时5年发展, 从swift1.x发展到了swift5.x版本, 经历了多次重大改变, ABI终于稳定.

    API(Application Programming Interface): 应用程序编程接口
        源代码和库之间的接口
    ABI(Application Binary Interface): 应用程序二进制接口
        应用程序与操作系统之间的底层接口
        设计的内容有: 目标文件格式(maco格式)、数据类型大小/布局/对齐,函数调用约定等等
    
    随着ABI的稳定, swift语法基本不会再有太大的变动, 此时正在学习swift的最佳时刻
    

    编译器分前端和后端:

    1.前端: 语法分析...
    2.后端:生成对应平台的二进制代码
    
    编译流程:
    OC
    c --> (编译前端)clang --> (编译后端)LLVM IR 通过 LLVM compiler --->x86/ARM/other
    Swift
    Swift --> (编译前端) swiftc --> (编译后端)LLVM IR 通过 LLVM compiler --->x86/ARM/other
    
    Swift编译大概流程:
    Swift Code(swift代码) --> Swift AST(根据swiftc生成语法树) --> Raw Swift IL(swift特有中间代码) --> Canonical Swift IL(swift特有中间代码更简洁) --> LLVM IR(编译后端生成LLVM中间代码) --> Assembly(汇编代码) --> Executable(二进制代码)
    

    swiftc

    swiftc存放在Xcode内部
    Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
    
    一些操作
    1.生成.swift文件的语法树
      命令行输入:cd (.swift所在文件目录)
      swiftc -dump-ast main.swift
    2.生成最简洁的SIL代码: swiftc -emit-sil main.swift
    3.生成LLVM IR代码: swiftc -emit-ir main.swift -o main.ll
    4.生成汇编代码: swiftc -emit-assembly main.swift -o main.s
    
    对汇编代码进行分析, 可以真正掌握编程语言本质
    
    断点后,转汇编代码查看: Debug >> Debug Workflow >> AlwaysShow Dissassembly(永远显示反汇编,就是把你平时写的代码转换成汇编代码)
    

    第一章 基本运算、流程控制、函数**

    1. Hello World

    1.不用编写main函数, Swift将全局范围内的首句可执行代码作为程序入口
    2.一句代码尾部可以省略分号(;), 多句代码写到同一行时必须用分号(;)隔开
    3.用var定义变量, let定义常量, 编译器能自动推断出变量/常量的类型
    4.Playground可以快速预览代码效果,是学习语法的好帮手
    
    快捷键:
    Command + Shift + Enter: 运行整个Playground
    
    var a = 10
    print("Hello Wrold! - \(a)")//拼接字符串
    

    2. Playground

    快捷键:
    Command + 1 显示左测文件栏
    Command + 0 隐藏左侧文件栏
    Command + N 新建代码文件page页
    
    import UIKit
    import PlaygroundSupport
    
    //使用Playground展示一个view
    let view = UIView()
    view.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    view.backgroundColor = UIColor.red
    PlaygroundPage.current.liveView = view
    
    //使用Playground展示一个图片
    let imageView = UIImageView(image: UIImage(name: "logo"))
    PlaygroundPage.current.liveView = imageView
    
    //使用Playground展示一个视图控制器
    let vc = UITableViewController()
    vc.view.backgroundColor = UIColor.lightGray
    PlaygroundPage.current.liveView = vc
    
    //Playground--多Page
    File >> New >> Playground Page
    

    3. 注释

    0. Swift支持注释嵌套
    
    // 单行注释
    
    /*
    多行注释
    */
    
    /*
    1
    // 多行注释嵌套单行注释
    /* 多行注释嵌套多行注释 */
    2
    */
    
    1.Playground的注释支持markup语法(与markdown类似) //: 或 /*:*/
        单行markup:
        编写markup语法 //: # 一级标题
    
        多行markup:
        编写markup语法
        /*:
                # 学习Swift
                ## 基础语法
                - 变量
                - 常量
                ## 面向对象
                - 类
                - 属性
        */
        运行markup语法 Editor >> Show Rendered Markup
    

    4. 常量

    // 只能赋值1次
    // 它的值不要求在编译时期确定, 在运行时确定也可以, 但使用之前必须赋值1次
    let age1 = 10
    
    let age2: Int //这种写法,必须说明类型
    age2 = 20
    
    func getAge() -> Int {
      return 30
    }
    let age3 = getAge()//在运行时确认值
    
    // 常量、变量在初始化之前, 都不能被使用, 否则会报错
    let age: Int
    var height: Int
    print(age)//报错
    print(height)//报错
    

    5. 标识符_数据类型

    // 标识符(比如常量名、变量名、函数名)几乎可以使用任何字符
    // 标识符不能以数字开头, 不能包含空白字符、制表符、箭头等特殊字符
    
    常见的数据类型: Swift常见数据类型.png
    整数类型: Int8、Int16、Int32、Int64、UInt8、UInt16、UInt32、UInt64
    在32bit平台, Int等价于Int32, 在64bit平台, Int等价于Int64
    整数的最值: UInt8.max、Int16.min
    一般情况下, 都是直接使用Int即可
    
    浮点类型:Float, 32位, 精度只有6位; Double, 64位, 精度至少15位.
    

    6. 字面量

    // 布尔
    let bool = true // 取反是false
    
    // 字符串
    let string = "小哥哥"
    
    // 字符 (可存储ASCII字符、Unicode字符)
    let character: Character = "🐶"
    
    // 整数
    let intDecimal = 17 //十进制
    let intBinary = 0b10001 //二进制
    let intOctal = 0o21 // 八进制
    let intHexadecimal = 0x11 //十六进制
    
    // 浮点数
    let doubleDecimal = 125.0 //十进制,等价于1.25e2,  0.0125等价于1.25e-2
    let doubleHexadecimal1 = 0xFp2//十六进制, 意味着15x(2^2), 相当于十进制的60.0
    let doubleHexadecimal2 = 0xFp-2//十六进制,意味着15x(2^-2),相当于十进制的3.75
    //以下都是表示12.1875
    //十进制:12.1875、1.21875e1
    //十六进制: 0xC.3p0
    
    // 整数和浮点数可以添加额外的零或者添加下划线来增强可读性
        比如: 100_0000、1_000_000.000_000_1、000123.456
    
    // 数组
    let array = [1,3,5,7,9]
    
    // 字典
    let dictionary = ["age" : 18, "height" : 165]
    

    7. 类型转换

    // 整数转换
    let int1: UInt16 = 2_000
    let int2: UInt8 = 1
    let int3 = int1 + UInt16(int2)
    
    // 整数、浮点数转换
    let int = 3
    let double = 0.14159
    let pi = Double(int) + double
    let intPi = Int(pi)
    
    // 字面量可以直接相加, 因为数字字面量本身没有明确的类型
    let result = 3 + 0.14159
    

    8. 元组(tuple)

    let http404Error = (404, "Not Found") //将多种类型组合在一起, 赋值给元祖对象
    print("The status code is \(http404Error.0)")
    
    let(statusCode, statusMessage) = http404Error //接收元祖
    print("The status code is \(statusCode)")
    
    let(justTheStatusCode, _) = http404Error //不想接收的值,用_占位
    
    let http200Status = (statusCode: 200, description: "OK")
    print("The status code is \(http200Status.statusCode)")
    

    第二章 if-else和while、区间、Switch、函数

    1、if-else 和 while

    // 1.if-else
    /*
      if后面的条件可以省略小括号;
      条件后面的大括号不可以省略;
      if后面的条件只能是Bool类型;
    */
    
    // 2.while
    /*
      repeat-while相当于C语言中的do-while;
      这里不用num--,是因为从Swift3开始,去除了自增(++)、自减(--)运算符
    */
    var num = 5
    while num > 0{
      print("num is \(num)")
      num -= 1
    }//打印了5次
    
    var num = -1
    repeat {
      print("num is \(num)")
    }while num > 0 //打印了1次
    

    2-1、区间

    // 闭区间运算符: a...b 其实就是 a <= 取值 <= b 
    let names = ["a","b","c","d"]
    for i in 0...3 {
      print(name[I])
    }// a b c d
    
    for name in names[0...3]{// for-区间运算符用在数组上
      print(name)
    }// a b c d
    
    let range = 1...3
    for i in range {
      print(name[I])
    }// b c d
    
    let a = 1
    let b = 2
    for i in a...b{// 可以用变量来作为区间
      print(name[I])
    }// b c
    for i in a...3{
      print(name[I])
    }// b c d
    
    for var i in 1...3{// i 默认是let, 有需要时可以声明为var
      i += 5
      print(i)
    }//6 7 8
    
    for _ in 1...3 {// 如果用不到i, 用_占位
      print("for")
    }//打印了3次
    

    2-2、半开区间运算符: a..<b 其实就是 a <= 取值 < b

    for i in 1..<5 {
      print(i)
    }//1 2 3 4
    

    2-3、单侧区间: 让区间朝一个方向尽可能的远

    for name in names[2...] {
      print(name)
    }// c d
    
    for name in names[...2] {
      print(name)
    }// a b c
    
    for name in names[..<2] {
      print(name)
    }// a b
    
    let range = ...5
    range.contains(7)//false
    range.contains(4)//true
    range.contains(-3)//true
    

    2-4、区间类型

    let range1: ClosedRange<Int> = 1...3
    let range2: Range<Int> = 1..<3
    let range3: PartialRangeThrough<Int> = ...5
    
    //字符、字符串也能使用区间运算符, 但默认不能用在for-in中
    let stringRange1 = "cc"..."ff" //CloseRange<String>
    stringRange1.contains("cb")//false
    stringRange1.contains("dz")//true
    stringRange1.contains("fg")//false
    
    let stringRange2 = "a"..."f"
    stringRange2.contains("d")//true
    stringRange2.contains("h")//false
    
    // \0到~囊括了所有可能要用到的ASCII字符
    let characterRange: CloseRange<Character> = "\0"..."~"
    characterRange.contains("G")//true
    

    2-5、带间隔的区间值

    let hours = 11
    let hourInterval = 2
    // tickMark的取值: 从4开始, 累加2, 不超过11
    for tickMark in stride(from: 4, through: hours, by: hourInterval){
      print(tickMark)
    }// 4 6 8 10
    

    3、switch

    /*
        case、default后面不能写大括号{} ;
        默认可以不写break, 并不会贯穿到后面的条件;
        使用fallthrough可以实现贯穿效果;
        switch必须要保证能处理所有情况, 否则会报错;
        case、default后面至少要有一条语句, 如果不想做任何事, 加个break即可;
    */
    var number = 1
    switch number {
      case 1:
        print("number is 1")
        break
      case 2:
        print("number is 2")
        break
      default:
        print("number is other")
        break
    }// number is 1
    
    switch number {//默认可以不写break, 并不会贯穿到后面的条件
      case 1:
        print("number is 1")
        fallthrough//使用fallthrough可以实现贯穿效果
      case 2:
        print("number is 2")
      default:
        break//case、default后面至少要有一条语句;
    }// number is 1  \n number is 2
    
    /*
        switch注意点:
            如果能保证已处理所有情况, 也可以不必使用default;
    */
    enum Answer {case right, wrong}
    let answer = Answer.right
    switch answer {
      case Answer.right:
        print("right")
      case Answer.wrong:
        print("wrong")
    }
    
    switch answer {//由于已确定answer是Answer类型, 因此可以省略Answer
      case .right:
        print("right")
      case .wrong:
        print("wrong")
    }
    

    3-1、复合条件

    //switch也支持Character、String类型
    let string = "Jack"
    switch string {
      case "Jack", "Rouse"://满足其中一个条件就执行
        print("Right person")
      default:
        break
    }//Right person
    

    3-2、区间匹配、元组匹配

    //可以使用下划线_忽略某个值
    //关于case匹配问题, 属于模式匹配的范畴,以后会再次详细展开讲解
    let count = 62
    switch count {
      case 0:
        print("none")
      case 1..<5: //区间匹配
        print("a few")
      case 5..<12:
        print("several")
      case 12..<100:
        print("dozens of")
      case 100..<1000:
        print("hundreds of")
      default:
        print("many")
    }// dozens of
    
    let point = (1, 1)
    switch point {
      case (0, 0)://元组匹配
        print("the origin")
      case (_,0):
        print("on the x-axis")
      case (0,_):
        print("on the y-axis")
      case (-2...2, -2...2):
        print("inside the box")
      default:
        print("outside of the box")
    }// inside the box
    

    3-3、值绑定

    let point = (2, 0)
    switch point {
      case(let x, 0):
        print("on the x-axis with an x value of \(x)")
      case(0, let y):
        print("on the y-axis with an y value of \(y)")
      case let(x, y):
        print("somewhere else at (\(x), \(y))")
    }// on the x-axis with an x value of 2
    

    3-4、where--过滤不满足条件的信息

    let point = (1, -1)
    switch point {
      case let (x,y) where x==y: //值绑定后,加条件
        print("on the line x==y")
      case let (x,y) where x==-y:
        print("on the line x==-y")
      case let (x,y):
        print("(\(x), \(y)) is just some arbitrary point")
    }// on the line x == -y
    
    //将所有正数加起来
    var numbers = [10,20,-10,-20,30,-30]
    var sum = 0
    for num in numbers where num > 0 {//使用where来过滤num
      sum += num
    }
    print(sum) //60
    

    3-5、标签语句

    outer: for i in 1...4 {// 如果现在内层嵌套的for循环里面写continue和break想控制外面的for循环,那么加上outer标签就好了
      for k in 1...4 {
        if k==3 {
          continue outer
        }
        if i==3 {
          break outer
        }
        print("i == \(i), k == \(k)")
      }
    }
    

    4、函数

    func pi() --> Double {//不带参数, 有返回值
      return 3.14
    }
    
    func sum(v1: Int, v2: Int) -> Int {//带参数, 有返回值
      return v1 + v2
    }
    sum(v1: 10,v2: 20)
    // 形参默认是let, 也只能是let
    
    func sayHello() -> Void {//写法一:无返回值
      print("Hello")
    }
    func sayHello() -> () {//写法二:无返回值, 小括号代表空元组
      print("Hello")
    }
    func sayHello() {//写法三:无返回值
      print("Hello")
    }
    

    4-1、隐式返回(Implicit Return)

    //如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式
      func sum(v1: Int, v2: Int) -> Int {
        v1 + v2
      }
      sum(v1: 10, v2: 20)// 30
    

    4-2、返回元组: 实现多返回值

    func calculate(v1: Int, v2: Int) -> (sum:Int, difference: Int, average: Int){
      let sum = v1 + v2
      return (sum, v1 - v2, sum >> 1)
    }
    let result = calculate(v1: 20, v2: 10)
    result.sum //30
    result.difference //10
    result.average //15
    

    4-3、函数的文档注释

    /// 求和[概述]
    ///
    /// 将两个整数相加[更详细的描述]
    ///
    /// - Parameter v1: 第1个整数
    /// - Parameter v2: 第2个整数
    /// - Returns: 2个整数的和
    ///
    /// - Note:传入两个整数即可[批注]
    ///
    func sum(v1: Int, v2: Int) -> Int {
      v1 + v2
    }
    // option + command + / 快速注释快捷键
    // 参考: https://swift.org/documentation/api-design-guidelines/
    

    4-4、参数标签(Argument Label)

    //可以修改参数标签
    func goToWork(at time:String) {//调用时at替代time作为参数名
      print("this time is \(time)")
    }
    goToWork(at: "00:00")//this time is 00:00
    
    //可以使用下划线_ 省略参数标签
    func sum(_ v1: Int, _ v2: Int) -> Int {
      v1 + v2
    }
    sum(10, 20)
    

    4-5、默认参数值(Default Parameter Value)

    //参数可以有默认值
    func check(name: String = "nobody", age: Int, job: String = "none"){
      print("name=\(name), age=\(age), job=\(job)")
    }
    check(name: "Jack")
    check(age: 10)
    //C++的默认参数值有个限制: 必须从右往左设置. 由于Swift拥有参数标签, 因此并没有此类限制
    //但是在省略参数标签时, 需要特别注意, 避免出错
    

    4-6、可变参数

    fuc sum(_ numbers:Int...) -> Int {//...代表可变参数,意味着可以传很多个Int参数
      var total = 0
      for number in numbers {//numbers暂且认定为数组
        total += number
      }
      return total
    }
    sum(10, 20, 30, 40)//100
    // 一个函数最多只能有1个可变参数
    // 紧跟在可变参数后面的参数不能用省略参数标签
    func test(_ numbers: Int..., string: String, _ other String){}
    test(10, 20, 30, string: "Jack", "Rose")
    

    4-7、输入输出参数(In-Out Parameter)

    // 可以用inout定义一个输入输出参数: 可以在函数内部修改外部实参的值
    func swapValues(_ v1: inout Int, _ v2: inout Int) {
      let tmp = v1
      v1 = v2
      v2 = tmp
    }
    var num1 = 10
    var num2 = 20
    swapValues(&num1, &num2)
    
    func swapValues(_ v1: inout Int, _ v2: inout Int) {//交换两个外部变量的值
      (v1, v2) = (v2, v1)//用元组进行交换
    }
    // 可变参数不能标记为inout
    // inout参数不能有默认值
    // 示例代码中inout参数的本质是地址传递(引用传递), 如果传递给inout参数的是计算属性、有监听器的属性等内容,其本质并非引用传递,在[属性]章节再作详细讲解
    // inout参数只能传入可以被多次赋值的,也就是说调用时传入v1和v2的,是可以被多次赋值的变量var
    

    4-8、函数重载(Function Overload)

    //规则: 函数名相同; 参数个数不同||参数类型不同||参数标签不同
    func sum(v1: Int, v2: Int) -> Int {
      v1 + v2
    }
    func sum(v1: Int, v2: Int, v3: Int) -> Int {
      v1 + v2 + v3
    }// 参数个数不同
    func sum(v1: Double, v2: Int) -> Double {
      v1 + Double(v2)
    }// 参数类型不同
    func sum(a: Int, b: Int) -> Int {
      a + b
    }//参数标签不同
    sum(v1: 10, v2: 20)//30
    sum(v1: 10, v2: 20, v3: 30)//60
    sum(v1: 10.0, v2: 20)//30.0
    sum(a: 10, b: 20)//30
    /*函数重载注意点:
      1.返回值类型与函数重载无关;
      2.默认参数值和参数重载一起使用产生二义性时,编译器并不会报错(在C++中会报错);
      3.可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器有可能会报错
    */
    

    4-9、内联函数(Inline Function)

    //如果开启了编译器优化(Release模式默认会开启优化), 编译器会自动将某些函数变成内联函数.
    //开启:Build Settings >> Swift Compiler - Code Generation >> Optimization Level >> Debug   Optimize for Speed[-O]
    //内联函数实质: 将函数调用展开成函数体
    //哪些函数不会被内联? 1.函数体比较长的时候 2.包含递归调用 3.包含动态派发(动态绑定)
    @inline(never) func test(){//永远不会被内联(即使开启了编译器优化)
      print("test")
    }
    @inline(__always) func test() {//开启编译器优化后, 即使代码很长,也会被内联(递归调用很熟,动态派发的函数除外)
      print("test")
    }
    //在Release模式下,编译器已经开启优化,会自动决定哪些函数需要内联,因此没必要使用@inline
    

    4-10、函数类型(Function Type)

    //每一个函数都有类型的, 函数类型由形式参数类型、返回值类型组成
    func test(){ }// () -> Void 或者 () -> ()
    
    func sum(a:Int, b:Int) -> Int{
      a + b
    }// (Int, Int) -> Int
    //定义变量
    var fn: (Int, Int) -> Int = sum
    fn(2, 3) //5 调用时不需要参数标签
    
    //函数类型作为函数参数
    func sum(v1: Int, v2: Int) -> Int {
      v1 + v2
    }
    func difference(v1: Int, v2: Int) -> Int {
      v1 - v2
    }
    func printResult(_ mathFn: (Int, Int) -> Int, _ a: Int, _ b: Int) {
      print("Result:\(mathFn(a, b))")
    }
    printResult(sum, 5, 2) // Result:7
    printResult(difference, 5, 2) // Result:3
    
    //函数类型作为函数返回值
    func next(_ input: Int) -> Int {
      input + 1
    }
    func previous(_ input: Int) -> Int {
      input - 1
    }
    func forward(_ forward: Bool) -> (Int) -> Int {
      forward? next : previous
    }
    forward(true)(3) //4
    forward(false)(3)//2  //每一个函数都有类型的, 函数类型由形式参数类型、返回值类型组成
    func test(){ }// () -> Void 或者 () -> ()
    
    func sum(a:Int, b:Int) -> Int{
      a + b
    }// (Int, Int) -> Int
    //定义变量
    var fn: (Int, Int) -> Int = sum
    fn(2, 3) //5 调用时不需要参数标签
    
    //函数类型作为函数参数
    func sum(v1: Int, v2: Int) -> Int {
      v1 + v2
    }
    func difference(v1: Int, v2: Int) -> Int {
      v1 - v2
    }
    func printResult(_ mathFn: (Int, Int) -> Int, _ a: Int, _ b: Int) {
      print("Result:\(mathFn(a, b))")
    }
    printResult(sum, 5, 2) // Result:7
    printResult(difference, 5, 2) // Result:3
    
    //函数类型作为函数返回值
    func next(_ input: Int) -> Int {
      input + 1
    }
    func previous(_ input: Int) -> Int {
      input - 1
    }
    func forward(_ forward: Bool) -> (Int) -> Int {
      forward? next : previous
    }
    forward(true)(3) //4
    forward(false)(3)//2
    

    4-11、typealias用来给类型起别名

    typealias Byte = Int8
    typealias Short = Int16
    typealias Long = Int64
    
    typealias Date = (year: Int, month:Int, day:Int)//用Date代替这个元组
    func test(_ date: Date) {
      print(date.0)
      print(date.year)
    }
    test((2011, 9, 10))
    
    typealias IntFn = (Int, Int) -> Int //用IntFn代替这种函数类型
    func difference(v1: Int, v2: Int) -> Int {
      v1 - v2
    }
    let fn: IntFn = difference
    fn(20, 10)//10
    func setFn(_ fn: IntFn){ }
    setFn(difference)
    func getFn() -> IntFn { difference }
    
    //按照Swift标准库的定义, Void就是空元组()
    public typealias Void = ()
    

    4-12、嵌套函数(Nested Function)

    //将函数定义在函数内部
    func forward(_ forward: Bool) -> (Int) -> Int {
      func next(_ input: Int) -> Int {
        input + 1
      }
      func previous(_ input: Int) -> Int {
        input - 1
      }
      return forward ? next : previous
    }
    forward(true)(3)//4
    forward(false)(3)//2
    

    第三章 枚举

    1-1、枚举的基本用法

    enum Direction {//定义方式一
      case north
      case south
      case east
      case west
    }
    enum Direction {//定义方式二
      case north, south, east, west
    }
    
    var dir = Direction.west //使用方式一
    dir = Direction.east
    dir = .north
    print(dir) // north
    
    switch Direction {//使用方式二
      case .north
        print("north")
      case .south
        print("south")
      case .east
        print("east")
      case .west
        print("west")
    }
    

    1-2、关联值(Associated Values)

    //有时会将枚举的成员值跟其他类型的关联存储在一起,会非常有用
    enum Score { //示例一
      case points(Int)
      case grade(Character)
    }
    var score = Score.points(96)
    score = .grade("A")
    switch score {
      case let .points(i):
        print(i,"points")
      case let .grade(i):
        print("grade", i)
    }// grade A
    
    enum Date {//示例二
      case digit(year: Int, month: Int, day: Int)
      case string(String)
    }
    var date = Date.digit(year: 2011 month: 9, day:10)
    date = .string("2011-09-10")
    switch date {
      case .digit(let year, let month, let day):
        print(year, month, day)
      case let .string(value):
        print(value)
    }
    

    1-3、原始值(Raw Values)

    // 枚举成员可以使用相同类型的默认值预先关联, 这个默认值叫做: 原始值
    enum PokerSuit : Character {
      case spade = "♠"
      case heart = "♥"
      case diamond = "♦"
      case club = "♣"
    }
    var suit = PokerSuit.spade
    print(suit) //spade
    print(suit.rawValue)//♠
    print(PokerSuit.club.rawValue)// ♣
    
    enum Grade: String {//枚举Grade关联的原始值是String类型
            case perfect = "A"
        case great = "B"
        case good = "C"
        case bad = "D"
    }
    print(Grade.perfect.rawValue) // A
    print(Grade.great.rawValue) // B
    print(Grade.good.rawValue) // C
    print(Grade.bad.rawValue) // D
    

    1-4、隐式原始值(Implicitly Assigned Raw Values)

    //如果枚举的原始值类型是Int、String, Swift会自动分配原始值
    enum Direction : String {
      case north = "north"
      case south = "south"
      case east = "east"
      case west = "west"
    }
    enum Direction: String {//等价于
      case north, south, east, west
    }
    print(Direction.north) //north
    print(Direction.north.rawValue) //north
    
    enum Season : Int { //自动递增
      case spring, summer, autumn, winter
    }
    print(Season.spring.rawValue) //0
    print(Season.summer.rawValue) //1
    print(Season.autumn.rawValue) //2
    print(Season.winter.rawValue) //3
    
    enum Season : Int { //自动递增
      case spring = 1, summer, autumn = 4, winter
    }
    print(Season.spring.rawValue) //1
    print(Season.summer.rawValue) //2
    print(Season.autumn.rawValue) //4
    print(Season.winter.rawValue) //5
    

    1-5、递归枚举

    //定义一个枚举类型,枚举的成员里面的关联值也用到了这个枚举类型,就是递归枚举.必须加关键字indirect
    indirect enum ArithExpr {//递归枚举写法一(多用)
      case number(Int)
      case sum(ArithExpr, ArithExpr)
      case difference(ArithExpr, ArithExpr)
    }
    enum ArithExpr {//递归枚举写法二
      case number(Int)
      indirect case sum(ArithExpr, ArithExpr)
      indirect case difference(ArithExpr, ArithExpr)
    }
    
    let five = ArithExpr.number(5)//获取枚举成员变量
    let four = ArithExpr.number(4)
    let two = ArithExpr.number(2)
    let sum = ArithExpr.sum(five, four)
    let difference = ArithExpr.difference(sum, two)
    
    func calculate(_ expr: ArithExpr) -> Int {
      switch expr {
        case let .number(value):
            return value
        case let .sum(left, right):
            return calculate(left) + calculate(right)
        case let .difference(left, right):
            return calculate(left) - calculate(right)
      }
    }
    calculate(difference)//使用递归枚举
    

    2、MemoryLayout

    可以使用MemoryLayout获取数据类型所占用的内存大小
    var age = 10
    //使用方法一: MemoryLayout支持泛型
    MemoryLayout<Int>.size //占8个字节数(分配占用的空间大小)
    MemoryLayout<Int>.stride //占8个字节(实际用到的空间大小)
    MemoryLayout<Int>.alignment //(alignment:内存对齐参数),占用8个字节
    
    //使用方法二:
    MemoryLayout.size(ofValue: age)
    MemoryLayout.stride(ofValue: age)
    MemoryLayout.alignment(ofValue: age)
    
    
    enum Password {
      case number(Int, Int, Int, Int)//此处是关联值,关联值会存储到枚举中去,占用32个字节
      case other//1
    }
    var pwd = Password.number(5,6,4,7) //编译器分配给它多少内存? 32
    pwd = .other //因为是同一个变量,所以还是占用32个字节
    MemoryLayout<Password>.size //占33个字节数,(实际用到的空间大小)
    MemoryLayout<Password>.stride //占40个字节,(分配占用的空间大小)
    MemoryLayout<Password>.alignmen //占8个字节,(对齐参数,此时要求分配空间要是8的倍数)
    
    enum Season {
      case spring = 1, summer, autumn, winter //此处是原始值,它是不会存储到枚举变量里面去的,底层用1个字节存储就可以了
    }
    MemoryLayout<Int>.size //1
    MemoryLayout<Int>.stride //1
    MemoryLayout<Int>.alignment//1
    //为什么是1个字节? 因为是关联值与原始值的区别: 原始值(定义成员的时候就给它一个默认值),原始值会永远跟你的成员保存在一起,原始值是固定死的,不允许传值; 关联值的特点是允许自己传入不同的值进来.
    总结: 如果枚举变量是关联值,到时候可以传入一个具体的值和枚举进行关联,那么传进去的这些值是直接存储到枚举变量内存里面; 如果枚举类型写个:Int之类的什么类型的,叫做原始值,原始值是跟每一个成员是固定绑在一起的, 但是这些原始值是不会占用你的枚举变量的内存的, 相当于我们的原始值不是存储在枚举变量的内存里面的.
    

    3、可选项(Optional)

    //可选项, 一般也叫可选类型, 它允许将值设置为nil
    //在类型名称后面加个?来定义一个可选项
    var name: String? = "Jack"//默认情况下是不允许你给空值的,只有设置为可选项才可以设置为nil
    name = nil
    
    var age: Int? //默认就是nil
    age = 10
    age = nil
    
    var array = [1, 15, 40, 29]
    func get(_ index: Int) -> Int? {//表明可以返回nil或Int类型
      if(index < 0 || index >= array.count){
        return nil
      }
      return array[index]
    }
    
    print(get(1)) // Optional(15)
    print(get(-1))// nil
    print(get(4))// nil
    

    3-1、强制解包(Forced Unwrapping)

    /*
        可选项是对其他类型的一层包装, 可以将它理解为一个盒子,
        如果为nil, 那么它是个空盒子,
        如果不为nil, 那么盒子里装的是: 被包装类型的数据;
        如果要从可选项中取出被包装的数据(将盒子里装的东西取出来), 需要使用感叹号!进行强制解包.
    */
    var age: Int? = 10
    let ageInt: Int = age!
    ageInt += 10
    //如果对值nil的可选项(空盒子)进行解包, 将会产生运行时错误. 所以强制解包时一定要确认它不是nil
    

    3-2、判断可选值是否包含值

    let number = Int("123")
    if number != nil {
       print("字符串转换整数成功:\(number!)")
    }else{
      print("字符串转换整数失败")
    }// 字符串转换整数成功:123
    

    3-3、可选项绑定(Optional Binding)

    // 可以使用可选项绑定来判断可选项是否包含值
    // 如果包含就自动解包,把值赋给一个临时的常量(let)或者变量(var),并返回true,否则返回false
    if let number = Int("123") {
      print("字符串转换成功:\(number)")
      // number是强制解包之后的Int值
      // number作用域仅限于这个大括号
    }else{
      print("字符串转换整数失败")
    }// 字符串转换整数成功:123
    
    enum Season : Int {
      case spring = 1, summer, autumn, winter
    }
    if let season = Season(rawValue: 6) {
      switch season {
        case.spring:
            print("the season is spring")
        default:
            print("the season is other")
      }
    } else {
      print("no such season")
    }// no such season
    
    // 等价写法:
    if let first  = Int("4") {
      if let second = Int("42"){
        if first < second && second < 100 {
          print("\(first) < \(second) < 100")
        }
      }
    }// 4 < 42 < 100
    
    if let first  = Int("4"),
            let second = Int("42"),
            first < second && second < 100 {
        print("\(first) < \(second) < 100")
    }// 4 < 42 < 100
    

    3-4、while循环中使用可选项绑定

    // 遍历数组, 将遇到的正数都加起来, 如果遇到负数或者非数字, 停止遍历
    var strs = ["10", "20", "abc", "-20", "30"]
    
    var index = 0
    var sum = 0
    while let num = Int(strs[index]), num > 0 {
      sum += num
      index += 1
    }
    print(sum)//30
    

    3-5、空合并运算符??(Nil-Coalescing Operator)

    public func ?? <T>(optional:T?,defaultValue: @autoclosure() throws -> T?) rethrows -> T?
    public func ?? <T>(optional:T?,defaultValue: @autoclosure() throws -> T) rethrows -> T
    
    a ?? b //a是可选项,??左边必须放可选项; b是可选项或者不是可选项; b跟a的存储类型必须相同; 作用:如果a不为nil,就返回a,此时如果b不是可选项,返回a时会自动解包;如果a为nil,就返回b,
    
    let a: Int? = 1
    let b: Int? = 2
    let c = a ?? b // c是Int? ,Optional(1)
    
    let a: Int? = nil
    let b: Int? = 2
    let c = a ?? b // c是Int? ,Optional(2)
    
    let a: Int? = nil
    let b: Int? = nil
    let c = a ?? b // c是Int? ,nil
    
    let a: Int? = 1
    let b: Int = 2
    let c = a ?? b // c是Int ,1
    
    let a: Int? = nil
    let b: Int = 2
    let c = a ?? b // c是Int ,2
    
    let a: Int? = nil
    let b: Int = 2
    //如果不使用??运算符
    let c : Int
    if let tmp = a {
      c = tmp
    }else {
      c = b
    }
    
    // 多个 ?? 一起使用
    let a: Int? = 1
    let b: Int? = 2
    let c = a ?? b ?? 3 // c是Int , 1 , c是什么类型要看最后一个运算符是什么类型.
    
    let a: Int? = nil
    let b: Int? = 2
    let c = a ?? b ?? 3 // c是Int , 2
    
    let a: Int? = nil
    let b: Int? = nil
    let c = a ?? b ?? 3 // c是Int , 3
    
    ??跟if let配合使用
    let a: Int? = nil
    let b: Int? = 2
    if let c = a ?? b {
      print(c)
    }//类似于 if a != nil || b != nil
    
    if let c = a, let d = b{
      print(c)
      print(d)
    }//类似于if a != nil && b != nil
    

    3-6、if语句实现登录

    func login(_ info:[String: String]) {
      let username: String
      if let tmp = info["username"] {
        username = tmp
      } else {
        print("请输入用户名")
        return
      }
      let password: String
      if let tmp = info["password"] {
          password = tmp
      } else {
        print("请输入密码")
        return
      }
      // if username ....
      // if password ....
      print("用户名:\(username)","密码:\(password)","登录ing")
    }
    
    login(["username" : "jack","password":"123456"])//用户名:jack 密码:123456 登录ing
    login(["password":"123456"])// 请输入用户名
    login(["username" : "jack"])// 请输入密码
    

    3-7、字典和数组的元素的返回值类型

    var dict = ["age":10]
    var age = dict["abc"]// Int?  字典返回可选类型
    
    var array = [1,2,3]
    var num = array[-1]// Int 数组直接返回值, 需要自己检查数组越界
    

    3-8、guard语句

    guard 条件 else {
        //do something...
      退出当前作用域
      //return、break、continue、throw、error
    }
    
    //当guard语句的条件为false时, 就会执行大括号里的代码;
    //当guard语句的条件为true时, 就会跳过guard语句
    //guard语句特别适合用来"提前退出"
    //当使用guard语句进行可选项绑定时,绑定的常量(let)、变量(var)也能在外层作用域中使用
    
    //用guard简写3-7的登录逻辑:
    func login(_ info:[String: String]){
        guard let username = info["username"] else{
            print("请输入用户名")
            return
        }
        guard let password = info["password"] else {
            print("请输入密码")
            return
        }
        //if username ....
        //if password ....
        print("用户名:\(username)","密码:\(password)","登录ing")
    }
    login(["username" : "jack","password":"123456"])//用户名:jack 密码:123456 登录ing
    login(["password":"123456"])// 请输入用户名
    login(["username" : "jack"])// 请输入密码
    

    3-9、隐式解包(Implicitly Unwrapped Optional)

    // 在某些情况下, 可选项一旦被设定值之后, 就会一直拥有值
    // 在这种情况下, 可以去掉检查, 也不必每次访问的时候都进行解包, 因为它能够确定每次访问的时候都有值
    // 可以在类型后面加个感叹号 ! 定义一个隐式解包的可选项
    let num1: Int! = 10 // 隐式解包的可选项(自动解包), 等价于:let num2: Int = num1!
    let num2: Int = num1
    if num1 != nil {
        print(num1 + 6)// 16
    }
    if let num3 = num1 {
        print(num3)// 10
    }
    
    /// 如果可选项是空值那么会报错
    let num1: Int! = nil
    let num2: Int = num1// Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file MyPlayground.playground, line 
    

    3-10、字符串插值

    // 字符串插值
    // 可选项在字符串插值或者直接打印时, 编译器会发出警告
    var age: Int? = 10
    print("My age is \(age)")//My age is Optional(10)
    
    // 至少有3种方法消除警告
    print("My age is \(age!)")//My age is 10
    print("My age is \(String(describing: age))")//My age is Optional(10)
    print("My age is \(age ?? 0)")// My age is 10
    

    3-11、多重可选项

    var num1: Int? = 10 //包装了一个Int类型的可选类型盒子
    var num2: Int?? = num1 // 包装了一个可选类型的可选类型盒子
    var num3: Int?? = 10
    print(num2 == num3) // true
    
    var aum1: Int? = nil
    var aum2: Int?? = aum1
    var aum3: Int?? = nil
    print(aum2 == aum3) // false
    
    (aum2 ?? 1) ?? 2 // 2
    (aum3 ?? 1) ?? 2 // 1
    
    /*
    可以使用lldb指令: help frame 查看frame有哪些指令
    查看内存布局:
    frame variable -R 或者 fr v -R 查看内存布局的区别
    比如:fr v -R aum1
    */ 
    

    3-12、思考下面枚举变量的内存布局

    enum TestEnum {
        case test1, test2, test3
    }
    // 1个字节存储成员值
    // N个字节存储关联值(N取占用内存最大的关联值), 任何一个case的关联值都共用这N个字节
    // 共用体
    
    // 有时候会将枚举的成员值跟其他类型的值关联存储在一起, 会非常有用
    
    //枚举总共能存放256个字节 0x00~0xFF
    
    var t = TestEnum.test1 //0
    t = .test2 //1
    t = .test3 //2
    
    print(MemoryLayout<TestEnum>.size)
    print(MemoryLayout<TestEnum>.stride)
    print(MemoryLayout<TestEnum>.alignment)
    
    // 查看变量内存存储情况
    // 打断点: Debug >> Debug Workflow >>View Memory  或者右键选择 View Memory of "xxx"
    

    第四章 汇编分析枚举的内存布局

    1-1、程序的本质

    软件/程序的执行过程:
        (硬盘)程序/软件-->(装载)内存--->(读写)--->CPU-->(控制)计算机
    
    CPU中分为:
        寄存器 --> 用来做:信息存储    
        运算器 --> 用来做:信息处理
        控制器 
    
    通常, CPU会先将内存中的数据存储到寄存器中, 然后再对寄存器中的数据进行运算
    
    假设内存中有块红色内存空间的值是3,现在想把它的值加1, 并将结果存储到蓝色内存空间
        1.CPU首先会将红色内存空间的值放到rax寄存器中: movq 红色内存空间, %rax  //movq是将左边的值送给到右边
        2.然后让rax寄存器与1相加: addq $0x1, %rax
        3.最后将值赋值给内存空间: movq %rax, 蓝色内存空间
    
    CPU规定:
        1.不允许内存间直接交互数据,必须要通过CPU的寄存器 
        2.想做运算或计算也要拉倒CPU的寄存器中进行运算, 运算结束后再送回内存
    

    1-2、汇编语言的发展

    机器语言
        由0-1组成
    汇编语言(Assembly Language)
        用符号代替0和1, 比机器语言便于阅读和记忆
    高级语言
        C/C++/JavaScript/Python等, 更接近人类自然语言
    
    操作: 将寄存器BX的内容送入寄存器AX
        机器语言: 1000100111011000
        汇编语言: movm %bx, %ax
        高级语言: ax = bx; //这里只是举例,高级语言不能直接操作寄存器.
    
    高级语言-->(编译)汇编语言<-->(编译/反编译)机器语言-->(运行)计算机
    
    汇编语言与机器语言一一对应, 每一条机器指令都有与之对应的汇编指令;
    汇编语言可以通过编译得到机器语言, 机器语言可以通过反汇编得到汇编语言;
    高级语言可以通过编译得到汇编语言/机器语言, 但汇编语言/机器语言几乎不可能
    

    1-3、汇编语言的种类

    汇编语言严重依赖于电脑硬件设备, 相当于CPU的架构不同用的汇编就不同, 种类有:
        8086汇编(16bit) //2个字节
        x86汇编(32bit) //4个字节
        x64汇编(64bit) //8个字节
        ARM汇编(嵌入式、移动设备)
        ...
    
    x86、x64汇编根据编译器的不同, 有2种书写格式:
        Intel: Windows派系
        AT&T:   Unix派系
    
    作为iOS开发工程师, 最主要的汇编语言是
        AT&T汇编 --> iOS模拟器
        ARM汇编   --> iOS真机设备
    

    1-4、常见的汇编指令

    项目 AT&T Intel 说明
    寄存器命名 %rax rax
    操作数顺序 movq %rax, %rdx mov rdx, rax 将rax的值赋值给rdx
    常数/立即数 movq 3 %rax movq0x10, %rax mov rax 3 mov rax, 0x10 将3赋值给rax 将0x10赋值给rax
    内存赋值 movq $0xa, 0x1ff7(%rip) mov qword ptr [rip+0x1ff7], 0xa 将0xa赋值给地址为rip + 0x1ff7的内存空间
    取内存地址 leaq -0x18(%rbp), %rax lea rax, [rbp - 0x18] 将rbp - 0x18这个地址赋值给rax
    jmp指令 jmp *%rdx jmp 0x4001002 jmp *(%rax) jmp rdx jmp 0x4001002 jmp [rax] call和jmp写法类似,区别: jmp是跳转到某个地址去执行代码,一直到结束; 而call是跳到这个地址(call后面跟的一般是函数地址)去执行代码,然后配合ret(return)来配合使用
    操作数长度 movl %eax, %edx movb $0x10, %al leaw 0x10(%dx), %ax mov edx, eax mov al, 0x10 lea ax, [dx + 0x10] b = byte(8-bit) s = short(16-bit integer or 32-bit floating point) w = word(16-bit) i = long(32-bit integer or 64-bit floating point) q = quad(64 bit) t = ten bytes(80-bit flating point)

    备注: rip储存的是指令地址: 即CPU要执行的下一条指令地址就存储在rip中. rip += 正在执行指令的长度

    1-5、寄存器

    有16个常用的寄存器
        %rax、%rbx、%rcx、%rdx、%rsi、%rbp、%rsp
        %r8、%r9、%r10、%r11、%r12、%r13、%r14、%r15
    
    寄存器的具体用途
        %rax、rdx常用作为函数返回值使用
        %rdi、%rsi、%rdx、%rcx、%r8、%r9等寄存器常用于存放函数参数
        %rsp、%rbp用于栈操作
        rip作为指令指针
            存储着CPU下一条要执行的指令的地址
            一旦CPU读取一条指令,rip会自动指向下一条指令(存储下一套指令的地址)
    
    r开头: 64bit的寄存器, 只有8个字节
    e开头: 32bit的寄存器, 占4个字节
    ax,bx,cx : 16bit的寄存器, 占2个字节
    ah al bh bl: 占1个字节
    
    问: 64位的结构体怎么存储大于8个字节的数据呢,比如结构体对象?
    答: 对象存在内存中,结构体不能塞到寄存器里面去的,它就是放在内存中
    

    1-6、lldb常用指令

    读取寄存器的值:
        register read/格式
            register read/x
    
    修改寄存器的值:
        register write 寄存器名称 数值
            register write  rax 0
    
    读取内存中的值:
        x/数量-格式-字节大小 内存地址
            x/3xw 0x0000010
    
    修改内存中的值
        memory write 内存地址 数值
            memory write 0x0000010 10
    
    格式
        x是16进制, f是浮点型, d是十进制
    
    字节大小
        b - byte 1字节
        h - half word 2字节
        w - word 4字节
        g - giant word 8字节
    
    express表达式
        可以简写: expr表达式
        expression $rax
        expression $rax = 1
    
    po 表达式
    point 表达式
        po/x(格式) $rax
    
    thread step-over、next、n
        单步运行, 把子函数当做整体一步执行(源码级别)
    thread step-in、step、s
        单步运行, 遇到子函数会进入子函数(源码级别)
    thread step-inst-over、next、ni
        单步运行,把子函数当做整体一步执行(汇编级别)
    thread step-inst、stepi、si
        单步运行,遇到子函数会进入子函数(汇编级别)
    thread step-out、finish
        直接执行完当前函数所有代码, 返回到上一个函数(遇到断点会卡住)
    

    补充知识点:

    指令的内存地址 第几个字节           汇编指令
     0x10000d40    <+1>:    movq %rsp, %rbp
     
    imm立即数, 也就是字面量
    

    第五章 汇编分析结构体、类的内存布局

    1-1、结构体

    //在Swift标准库中, 绝大多数的公开类型都是结构体, 而枚举和类只占很小一部分
        //比如Bool、Int、Double、String、Array、Dictionary等常见类型都是结构体.
    ①struct Date {
    ②    var year: Int = 0
    ③  var month: Int
    ④  var day: Int
    ⑤}
    ⑥var date = Date(year: 2019, month: 6, day: 23)
    //所有的结构体都有一个编译器自动生成的初始化器(Initializer, 初始化方法、构造器、构造方法)
    //在第⑥行调用的, 可以传入所有成员值,用以初始化所有成员(存储属性, Stored Property)
    

    1-2、结构体的初始化器

    //编译器会根据情况, 可能会为结构体生成多个初始化器(比如给定成员初始值), 宗旨是: 保证所有成员都有初始值.
    struct Point {
      var x: Int?
      var y: Int?
    }
    var p1 = Point(x:10, y:10)
    var p2 = Point(y:10)
    var p3 = Point(x:10)
    var p4 = Point()
    //可选项都有个默认值nil, 因此上面代码可以编译通过!!
    

    1-3、自定义初始化器

    //一旦在自定义结构体时定义了初始化器, 编译器就不会再帮它自动生成其他初始化器.
    struct Point {
        var x: Int = 0
      var y: Int = 0
      init(x: Int, y: Int) {
        self.x = x
        self.y = y
      }
    }
    var p1 = Point(x: 10, y: 10)
    

    1-4、窥探初始化器的本质

    // 以下2端代码完全等效
    struct Point {
        var x: Int = 0
      var y: Int = 0
    }
    var p = Point()
    
    struct Point{
      var x: Int
      var y: Int
      init() {
        x = 0
        y = 0
      }
    }
    var p = Point()
    

    2-1、类

    //类的定义和结构体类似, 但编译器并没有为类自动生成可以传入成员值的初始化器
    class Point {
      var x: Int = 0
      var y: Int = 0
    }
    let p1 = Point()
    //如果类的所有成员都在定义的时候指定了初始值, 编译器会为类生成无参的初始化器
    //成员的初始化是在这个初始化器中完成的
    

    2-2、结构体与类的本质区别

    //结构体是值类型(枚举也是值类型), 类是引用类型(指针类型)
    class Size {
      var width = 1
      var height = 2
    }
    struct Point {
      var x = 3
      var y = 4
    }
    func test() {
      var size = Size()
      print("size变量的地址",Mems.ptr(ofVal: &size))
      print("size变量的内存",Mems.memStr(ofVal: &size))
      
      print("size所指向内存的地址",Mems.ptr(ofRef: size))
      print("size所指向内存的内容",Mems.memStr(ofRef: size))
      
      var point = Point() 
      print("point变量的地址",Mems.ptr(ofVal: &point))
      print("point变量的内存",Mems.memStr(ofVal: &point))
    }
    // 值类型的特点: 如果值类型是在函数里面创建的,那么它的内存肯定在栈空间里面
    // 指针变量在64bit栈空间内存中占几个字节? 8个字节,存放的是Size对象的内存地址.Size对象在堆空间. Size对象在堆空间占用32个字节内存,分别是引用计数(8个字节)、指针类型信息(8个字节).
    // 上面都是针对64bit环境
    // 汇编中有alloc、malloc相关的信息, 说明系统分配在堆空间.
    

    2-3、对象的堆空间申请过程

    //在Swift中, 创建类的实例对象, 要向堆空间申请内存, 大概流程如下:
        Class._allocating_init()
        libswiftCore.dylib:_swift_allocObject_
        libswiftCore.dylib:swift_slowAlloc
        libsystem_malloc.dylib:malloc
    
    //在Mac、iOS中malloc函数分配的内存大小总是16的倍数
    var ptr = malloc(1) //申请1个字节的堆空间
    print(malloc_size(ptr))// 16, 所指向的堆空间是16个字节
    var ptr = malloc(17) //申请17个字节的堆空间
    print(malloc_size(ptr))// 32, 分配给你的堆空间是32个字节
    
    class Size {
      var width = 1
      var height = 2
    }
    var size = Size()
    print(malloc_size(Mems.ptr(ofRef: size)))//size这个指针变量,它指向堆空间是32个字节
    
    //通过class_getInstanceSize可以得知: 类的对象至少需要占用多少内存
    class Point {
      var x = 11//8
      var text = true//1
      var y = 22//8
    }//实际利用的字节为33个
    var p = Point() // 要经过堆空间malloc检查, 是否为16的倍数
    class_getInstanceSize(type(of:p))//40 内存对齐后,对象至少要占多少内存
    class_getInstanceSize(Point.self)//40 对象占用多少内存,内存对齐是8
    print(Mems.size(ofRef:p))//48 堆空间真实分配内存空间的大小,堆空间分配空间的时候,内存对齐是16
    
    问:结构体都存在栈里?
    答: 结构体内存在哪里取决于你在哪里定义的. 如果你的结构体变量是在函数里面定义的,那么它的内存肯定就在栈空间; 如果你的结构体变量在外部定义的,它的内存就在数据段,也就是全局区,因为它是个全局变量; 假如结构体是在对象里面,那么这个结构体肯定跟随对象在堆空间. 同理枚举.
    

    2-4、值类型

    值类型赋值给var、let或者给函数传参, 是直接将所有内容拷贝一份.
        类似于对文件进行copy、paste操作,产生了全新的文件副本.属于深拷贝(deep copy)
    struct Point{
        var x: Int
      var y: Int
    }
    func test(){
      var p1 = Point(x: 10, y: 20)
      var p2 = p1
      p2.x = 11
      p2.y = 22
      //请问p1.x和p1.y是多少? 答: 10 20
    }
    
    /*规律: 
        内存地址格式为: 0x4bdc(%rip), 一般是全局变量, 全局区(数据段)
        内存地址格式为: -0x78(%rbp), 一般是局部变量, 栈空间
        内存地址格式为: 0x10(%rax), 一般是堆空间
    */
    
    //值类型赋值操作
    var s1 = "Jack"
    var s2 = s1
    s2.append("_Rose")
    print(s1)// Jack
    print(s2)// Jack_Rose
    
    /* 
    在Swift标注库中, 为了提升性能, String、Array、Dictionary、Set采用了Copy On Write的技术(也就是说当我们修改内存的时候,它才会进行深度拷贝操作,否则是浅拷贝).
            比如仅当有"写"操作时, 才会真正执行拷贝操作
            对于标准库值类型的赋值操作, Swift能确保最佳性能, 所以没必要为了保证最佳性能来避免赋值.
    */
    

    2-5、引用类型

    /*
    引用赋值给var、let或者给函数传参, 是将内存地址拷贝一份.
            类似于制作一个文件的替身(快捷方式、链接),指向的是同一个文件.属于浅拷贝(shallow copy)
    */
    class Size {
        var width: Int
        var height: Int
        init(width:Int, height:Int) {
          self.width = width
          self.height = height
        }
    }
    func test() {
        var s1 = Size(width: 10, height: 20)
        var s2 = s1
        s2.width = 11
        s2.height = 22
        //请问s1.width和s2.height是多少? 答:11 22
    }
    

    第六、七章 汇编分析闭包本质

    1-1、嵌套类型

    struct Poker {
      enum Suit : Character {
        case spades = "♠", hearts = "♥", diamonds="♦", clubs = "♣"
      }
      enum Rank : Int {
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace
      }
    }
    
    print(Poker.Suit.hearts.rawValue)
    
    var suit = Poker.Suit.spades
    suit = .diamonds
    
    var rank = Poker.Rank.five
    rank = .king
    

    1-2、枚举、结构体、类都可以定义方法

    一般把定义在枚举、结构体、类内部的函数, 叫做方法.
    struct Point {
        var x = 10
      var y = 10
      func show(){//方法是不占用实例对象内存的, 方法的本质就是函数, 函数、方法都存放在代码段
        var a = 10//a局部变量
        print("局部变量(栈空间)",Mems.ptr(ofVal: &a))
        print("x=\(x),y=\(y)")
      }
    }
    let p = Point()//Point()在堆空间; p全局变量,在数据段的地方;
    p.show() // x=10, y=10
    
    print("全局变量",Mems.ptr(ofVal:&p))
    print("堆空间",Mems.ptr(ofVal:p))
    

    2-1、闭包表达式( Closure Expression)

    /// 在Swift中, 可以通过func定义一个函数, 也可以通过闭包表达式定义一个函数
    func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2}
    // 简写一:
    var fn = {
        (v1: Int, v2: Int) -> Int in
        return v1 + v2
    }
    fn(10, 20)
    // 简写二:
    {
        (v1: Int, v2: Int) -> Int in
        return v1 + v2
    }(10, 20)
    
    /*闭包的格式写法:
    {
      (参数列表) -> 返回值类型 in
      函数体代码
    }
    */
    

    2-2、闭包表达式的简写

    func exec(v1: Int, v2: Int, fn:(Int, Int) -> Int) {
        print(fn(v1, v2))
    }
    // 简写一:
    exec(v1: 10, v2: 20, fn:{
        (v1: Int, v2: Int) -> Int in
        return v1 + v2
    })
    // 简写二: (省略参数类型)
    exec(v1: 10, v2: 20, fn: {
        v1, v2 in return v1 + v2
    })
    // 简写三: 
    exec(v1: 10, v2: 20, fn: {
        v1, v2 in v1 + v2
    })
    // 简写四:
    exec(v1: 10, v2: 20, fn: {  $0 + $1 })
    // 简写五:
    exec(v1: 10, v2: 20, fn: + )
    

    2-3、尾随闭包

    /*如果将一个很长的闭包表达式作为函数的最后一个实参, 使用尾随闭包可以增加函数的可读性
        尾随闭包是一个被书写在函数调用括号外面(后面)的闭包表达式
    */
    func exec(v1: Int, v2: Int, fn:(Int, Int) -> Int) {
        print(fn(v1, v2))
    }
    //尾随闭包写法:
    exec(v1: 10, v2: 20){
      $0 + $1
    }
    
    /*如果闭包表达式是函数的唯一实参, 而且使用了尾随闭包的语法, 那就不需要在函数名后边写圆括号
    */
    func exec(fn:(Int, Int) -> Int) {
        print(fn(v1, v2))
    }
    exec(fn: { $0 + $1 })// 普通调用
    exec() { $0 + $1 }// 尾随闭包写法
    exec { $0 + $1 }// 尾随闭包简写
    

    2-4、示例 - 数组的排序

    func testSort(){
      var arr = [10, 1, 4, 20, 99]
      arr.sort()
      //arr.sort(by:(Int, Int) throws -> Bool)
      print(arr)
    }
    
    /*解析:
        func sort(by areInIncreasingOrder: (Element, Element) -> Bool)
    */
    /// 返回true: i1排在i2前面
    /// 发挥false: i1排在i2后面
    func cmp(i1: Int, i2: Int) -> Bool {
      // 大的排在前面
      return i1 > i2
    }
    var nums = [11, 2, 18, 6]
    nums.sort(by: cmp)// [18, 11, 6, 2]
    // 简写一:
    nums.sort(by: {
        (i1: Int, i2: Int) -> Bool in
        return i1 < i2
    })
    // 简写二: 
    nums.sort(by: {i1, i2 in return i1 < i2})
    // 简写三: 
    nums.sort(by: {i1, i2 in i1 < i2})
    // 简写四: 
    nums.sort(by: { $0 < $1 })
    // 简写五: 
    nums.sort(by: <)
    // 简写六: 
    nums.sort(){ $0 < $1 }
    // 简写七: 
    nums.sort { $0 < $1 }
    

    3-1、闭包(Closure)

    /*网上有各种关于闭包的定义, 个人觉得比较严谨的定义是:
            一个函数和它所捕获的变量/常量环境组合起来, 称为闭包
        一般指的是定义在函数内部的函数;
        一般它捕获的是外层函数的局部变量/常量.
    */
    typealias Fn = (Int) -> Int //定义类型
    /// 闭包写法一: (通过func)
    func getFn() -> Fn{
        var num = 0
        func plus(_ i: Int) -> Int {
            num += i
            return num
        }
        return plus
    }// 返回的plus和num形成了闭包
    /// 闭包写法二: (通过闭包表达式)
    func getFn() -> Fn{
      var num = 0
      return {
        num += $0
        return num
      }
    }
    var fn1 = getFn()
    var fn2 = getFn()
    print(fn1(1)) // 1
    print(fn2(2)) // 2
    print(fn1(3)) // 4
    print(fn2(4)) // 6
    
    /*可以把闭包想象成一个类的实例对象
            内存在堆空间
            捕获的局部变量/常量就是对象的成员(存储属性)
            组成闭包的函数就是类内部定义的方法
    */
    class Closure {
        var num = 0
      func plus(_ i: Int) -> Int {
        num += i
        return num
      }
    }
    var cs1 = Closure()
    var cs2 = Closure()
    cs1.plus(1)//1
    cs2.plus(2)//2
    cs1.plus(3)//4
    cs2.plus(4)//6
    

    3-2、练习

    typealias Fn = (Int) -> (Int, Int)
    func getFns() -> (Fn, Fn) {
      var num1 = 0
      var num2 = 0
      func plus (_ i: Int) -> (Int, Int) {
        num1 += i
        num2 += i << 1 // <<1 等于 *2
        return (num1, num2)
      }
      func minus(_ i: Int) -> (Int, Int) {
        num1 -= i
        num2 -= i << 1
        return (num1, num2)
      }
      return (plus, minus)
    }
    let (p, m) = getFns()
    p(5) //(5,10)
    m(4) //(1,2)
    p(3) //(4,8)
    m(2) //(2,4)
    

    3-3、注意

    //如果返回值是函数类型, 那么参数的修饰要保持统一
    func add(_ num: Int) -> (inout Int) -> Void {
      func plus(v: inout Int) {//inout(输入输出参数), 与上面要保持统一
        v += num
      }
      return plus
    }
    var num = 5
    add(20)(&num)
    print(num)
    

    3-4、自动闭包

    // 如果第1个数大于0, 返回第一个数, 否则返回第2个数
    func getFirstPositive(_ v1: Int, _ v2: Int) -> Int {
        return v1 > 0 ? v1 : v2
    }
    getFirstPositive(10, 20) //10
    getFirstPositive(-2, 20) //20
    getFirstPositive(0, -4) //-4
    
    // 改成函数类型的参数, 可以让v2延迟加载
    func getFirstPositive(_ v1: Int, _ v2: () -> Int) -> Int? {
        return v1 > 0 ? v1 : v2()
    }
    getFirstPositive(-4) { 20 }
    
    func getFirstPositive(_ v1: Int, _ v2: @autoclosure () -> Int) -> Int? {
        return v1 > 0 ? v1 : v2()
    }
    getFirstPositive(-4, 20)
    getFirstPositive(-4, {30})
    // @autoclosure 会自动将20封装成闭包{ 20 }
    // @autoclosure 只支持() -> T 格式的参数
    // @autoclosure 并非只支持最后一个参数
    // 空合并运算符 ?? 使用了 @autoclosure 技术
    // 有@autoclosure、无@autoclosure, 构成了函数重载
    // 为了避免与期望冲突, 使用了@autoclosure的地方最好明确注释清楚, 这个值会被推迟执行
    

    第八章 属性、汇编分析inout本质

    1-1、属性

    /*Swift中跟实例相关的属性可以分为2大类
        存储属性( Stored Property)
                >类似于成员变量这个概念
                >存储在实例的内存中
                >结构体、类可以定义存储属性
                >枚举不可以定义存储属性
        计算属性( Computed Property)
                >本质就是方法(函数)
                >不占用实例的内存
                >枚举、结构体、类都可以定义计算属性
    */
    struct Circle {
        //存储属性
        var radius: Double
        //计算属性
        var diameter: Double {
            set {
              radius = newValue / 2
            }
            get {
              radius * 2
            }
        }
    }
    var c = Circle(radius: 10)
    c.radius = 11
    c.diameter = 40
    print(c.radius)// 20.0
    print(MemoryLayout<Circle>.stride)// 8
    

    1-2、存储属性

    /*关于存储属性, Swift有个明确的规定:
        在创建类或结构体的实例时, 必须为所有的存储属性设置一个合适的初始值
            >可以在初始化器里面为属性设置一个初始值
            >可以分配一个默认的属性值作为属性定义的一部分
    */
    

    1-3、计算属性

    /// set传入的新值默认叫做newValue, 也可以自定义
    struct Circle {
        var radius: Double
      var diameter: Double {
        set(newDiameter){
          radius = newDiameter / 2
        }
        get {
          radius * 2
        }
      }
    }
    
    /// 只读计算属性: 只有get, 没有set
    struct Circle {
        var radius: Double
      var diameter: Double {
        get {
          radius * 2
        }
      }
    }
    //简写:
    struct Circle {
        var radius: Double
      var diameter: Double { radius * 2 }
    }
    
    /* 定义计算属性只能用var, 不能用let. 
                >let代表常量.值是一成不变的
                >计算属性的值是可能发生变化的(即使是只读计算属性)
    */
    

    1-4、枚举rawValue原理

    /*枚举原始值rawValue的本质是: 只读计算属性*/
    enum TestEnum: Int {
        case test1 = 1, test2 = 2, test3 = 3
      var rawValue: Int {
        switch self {
          case .test1:
            return 10
          case .test2:
            return 11
          case .test3:
            return 12
        }
      }
    }
    print(TestEnum.test3.rawValue) //12
    

    1-5、延迟存储属性( Lazy Stored Property)

    /*使用lazy可以定义一个延迟存储属性, 在第一次用到属性的时候才会进行初始化*/
    class Car {
      init(){
        print("car init!")
      }
      func run(){
        print("car is running!")
      }
    }
    
    class Person{
      lazy var car = Car()
      init(){
        print("Persion init!")
      }
      func goOut(){
        car.run()
      }
    }
    let p = Persion()
    print("---------")
    p.goOut()
    
    
    class PhotoView {
      lazy var image: Image = {
        let url = "https://www.520it.com/xx.png"
        let data = Data(url: url)
        return Image(data: data)
      }()//闭包
    }
    
    /*
    lazy属性必须是var, 不能是let
            let必须在实例的初始化方法完成之前就拥有值
    如果多条线程同时第一次访问lazy属性
            无法保证属性只被初始化1次
    */
    /*延迟存储属性注意点:
    当结构体包含一个延迟属性时, 只有var才能访问延迟存储属性
            因为延迟属性初始化时需要改变结构体的内存
    */
    struct Point {
        var x = 0
      var y = 0
      lazy var z = 0
    }
    let p = Point()//此时必须用var修饰
    print(p.z)// 报错
    

    1-6、属性观察器(Property Observer)

    /*可以为非lazy的var存储属性设置属性观察器*/
    struct Circle {
        var radius: Double {//存储属性
        willSet {
          print("willSet", newValue)
        }
        didSet {
          print("didSet", oldValue, radius)
        }
      }
      init(){
        self.radius = 1.0
        print("Circle init!")
      }
    }
    var circle = Circle() // Circle init
    circle.radius = 10.5 //willSet 10.5  \n didSet 1.0 10.5
    print(circle.radius)// 10.5
    /*
      willSet会传递新增, 默认叫newValue
      didSet会传递旧值, 默认叫oldValue
      在初始化器中设置属性值不会触发willSet和didSet
            >在属性定义的时设置初始值也不会触发willSet和didSet
    */
    

    1-7、全局变量、局部变量

    /*属性观察器、计算属性的功能, 同样也可以应用在全局变量、局部变量身上*/
    var num: Int {
        get {
        return 10
      }
      set {
        print("setNum",newValue)
      }
    }
    num = 11 // setNum 11
    print(num)// 10
    
    
    func test() {
      var age = 10 {
        willSet {
          print("willSet", newValue)
        }
        didSet {
          Print("didSet", oldValue, age)
        }
      }
      age = 11
      // willSet 11
      // didSet 10 11
    }
    test()
    

    2-1、inout的再次研究

    struct Shape {
        var width: Int
      var side: Int { //带有属性观察器的储存属性
        willSet {
          print("willSetSide", newValue)
        }
        didSet {
          print("didSetSide", oldValue, side)
        }
      }
      var girth: Int {
        set {
          width = newValue / side
          print("setGirth", newValue)
        }
        get {
          print("getGirth")
          return width * side
        }
      }
      func show(){
        print("width=\(width),side=\(side),girth=\(girth)")
      }
    }
    
    func test(_ num: inout Int) { //接收一个地址值,输入输出参数inout本质是引用传递
      print("test")
      num = 20
    }
    
    var s = Shape(width: 10, side: 4)
    test(&s.width)
    s.show()
    print("-----------")
    test(&s.side)
    s.show()
    print("-----------")
    test(&s.girth)
    s.show()
    print("-----------")
    /*inout的本质总结:
    如果实参有物理内存地址, 且没有设置属性观察器
            直接将实参的内存地址传入函数(实参进行引用传递)
    如果实参是计算属性 或者 设置了属性的观察者
            采用了Copy In Copy Out的做法:
                    调用该函数时, 先复制实参的值, 产生副本[get]
                    将副本的内存地址传入函数(副本进行引用传递),在函数内部可以修改副本的值
                    函数返回后, 再将副本的值覆盖实参的值[set]
    总结: inout的本质就是引用传递(地址传递)
    */
    

    3-1、类型属性(Type Property)

    /*严格来说, 属性可以分为:
    
        实例属性(Instance Property): 只能通过实例去访问
                >存储实例属性(Stored Instance Property): 存储在实例内存中, 每个实例都有1份
                >计算实例属性(Computed Instance Property)
    
        类型属性(Type Property):只能通过类型去访问
                >存储类型属性(Stored Type Property): 这个程序运行过程中, 就只有1份内存(类似于全局变量)
                >计算类型属性(Computed Type Property)
    
    可以通过static定义类型属性
        如果是类, 可以通过关键字class. 不过关键字class不能修饰类中的存储属性
    */
    class Shape {
        var width: Int = 0 //实例属性
            static var count: Int = 0 // static关键字声明类型属性
    }
    var s = Shape()
    s.count = 10//报错
    Shape.count = 10
    
    struct Car {
      static var count: Int = 0
      init(){
        Car.count += 1
      }
    }
    let c1 = Car()
    let c2 = Car()
    let c3 = Car()
    print(Car.count)//3
    

    3-2、类型属性细节

    /*不同存储实例属性, 你必须给存储类型属性设定初始值
            >因为类型没有像实例那样的init初始化器来初始化存储属性
    存储类型属性默认就是lazy, 会在第一次使用的时候才初始化
            >就算被多个线程访问, 保证只会初始化一次
            >存储类型属性可以是let
    
    枚举类型也可以定义类型属性(存储类型属性、计算类型属性)
    */
    enum Shape {
        static var width: Int = 0 //可以定义类型属性, 不可以定义实例存储属性
      case s1, s2, s3, s4
    }
    var s = Shap.s1
    
    /*单例模式*/
    public class FileManager {
      public static let shared = FileManager()
      private init(){ 
       
      }
      func open() {
        
        }
        func close() {
          
        } 
    }
    FileManager.shared.open()
    FileManager.shared.close()
    

    第九章 汇编分析类型属性、方法、下标、继承

    1-1、方法(Method)

    /*枚举、结构体、类都可以定义实例方法、类型方法
            实例方法(Instance Method ) : 通过实例调用
            类型方法(Type Method): 通过类型调用, 用static或者class关键字定义
    */
    class Car {
      static var count = 0
      init() {
        Car.count += 1
      }
      static func getCount() -> Int { count }
    }
    let c0 = Car()
    let c1 = Car()
    let c2 = Car()
    print(Car.getCount()) //3
    /*
    self
            在实例方法中代表实例
            在类型方法中代表类型
    
    在类型方法中 static func getCount中
            count等价于self.count、Car.self.count、Car.count
    */
    

    1-2、mutating

    /*结构体和枚举是值类型, 默认情况下, 值类型的属性不能被自身的实例方法修改
        在func关键字前面加mutating可以允许这种修改行为
    */
    struct Point{
        var x = 0.0, y = 0.0
      mutating func moveBy(deltaX: Double, deltaY: Double) {
        x += deltaX
        y += deltaY
      }
    }
    
    enum StateSwitch {//状态开关
      case low, middle, high
      mutating func next() {
        switch self {
          case .low:
            self = .middle
          case .middle:
            self = .high
          case .high:
            self = .low
        }
      }
    }
    

    1-3、@discardableResult

    // 在func前面加个@discardableResult, 可以消除: 函数调用返回值未被使用的警告⚠️
    struct Point{
        var x = 0.0, y = 0.0
      @discardableRusult mutating func moveX(deltaX: Double) -> Double {
        x += deltaX
        return x
      }
    }
    var p = Point()
    p.moveX(deltaX: 10)
    
    @discardableResult func get() -> Int {
      return 10
    }
    get()
    

    2-1、下标(subscribe)

    /// 使用subscribe可以给任意类型(枚举、结构体、类)增加下标功能, 有些地方也翻译为: 下标脚本
    // subscript的语法类似于实例方法、计算属性,本质就是方法(函数)
    class Point{
      var x = 0.0, y = 0.0
      subscript(index: Int) -> Double {
        set {
          if index == 0 {
            x = newValue
          } else if index == 1{
            y = newValue
          }
        }
        get {
          if index == 0{
            return x
          } else if index == 1{
            return y
          }
          return 0
        }
      }
    }
    
    var p = Point()
    p[0] = 11.1 //下标set方法
    p[1] = 22.2
    print(p.x)//11.1
    print(p.y)//22.2
    print(p[0])//11.1  下标get方法
    print(p[1])//22.2
    
    /*subscript中定义的返回值类型决定了:
        get方法的返回值类型
        set方法中newValue的类型
    
    subscript可以接受多个参数,并且类型任意
    */
    

    2-2、下标的细节

    /// subscript可以没有set方法, 但必须要有get方法
    class Point {
      var x = 0.0, y = 0.0
      subscript(index: Int) -> Double {
        get {
          if index == 0 {
            return x
          } else if index == 1 {
            return y
          }
          return 0
        }
      }
    }
    /// 如果只有get方法, 可以省略get
    class Point {
        var x = 0.0, y = 0.0
      subscript(index: Int) -> Double {
        if index == 0{
          return x
        } else if index == 1{
          return y
        }
        return 0
      }
    }
    
    /// 可以设置参数标签
    class Point {
      var x = 0.0, y = 0.0
      subscript(index i: Int) -> Double {
        if i == 0 {
          return x
        } else if i == 1{
          return y
        }
        return 0
      }
    }
    var p = Point()
    p.y = 22.2
    print(p[index: 1]) // 22.2
    
    /// 下标可以是类型方法
    class  Sum {
      static subscript(v1: Int, v2: Int) -> Int {
        return v1 + v2
      }
    }
    print(Sum[10, 20]) //30
    

    2-3、结构体、类作为返回值对比

    class Point {
      var x = 0, y = 0
    }
    class PointManager {
      var point = Point()
      subscript(index: Int) -> Point {
        get { point }//只有get方法, 给x/y赋值时会报错
      }
    }
    
    
    struct Point {//结构体是值类型, 下标不写set方法会报错
      var x = 0, y = 0
    }
    class PointManager {
      var point = Point()
      subscript(index: Int) -> Point{
        set { point = newValue } 
        get { point }
      }
    }
    
    var pm = PointManager()
    pm[0].x = 11//本质: pm[0] = Point(x: 11, y: pm[0].y)
    pm[0].y = 22//本质: pm[0] = Point(x: pm[0].x, y: 22)
    // Point(x: 11, y:22)
    print(pm[0])
    //Point(x:11, y:22)
    print(pm.point)
    
    
    class Point {//类是引用类型, 下标不写set方法不会报错
      var x = 0, y = 0
    }
    class PointManager {
      var point = Point()
      subscript(index: Int) -> Point {
        get { point }//只有get方法, 给x/y赋值时不会报错
      }
    }
    

    2-4、接收多个参数的下标

    class Grid {
      var data = [
        [0,1,2],
        [3,4,5],
        [6,7,8]
      ]
      subscript(row: Int, column: Int) -> Int {
        set {
          guard row >= 0 && row < 3 && column >= 0 && column < 3 else{
            return
          }
          data[row][column] = newValue
        }
        get {
          guard row >= 0 && row < 3 && column >=0 && column < 3 else{
            return 0
          } 
          return data[row][column]
        }
      }
    }
    
    var grid = Grid()
    grid[0 ,1] = 77
    grid[1, 2] = 88
    grid[2, 0] = 99
    print(grid.data)
    

    3-1、继承(Inheritance)

    /// 值类型(枚举、结构体)不支持继承, 只有类支持继承
    
    /// 没有父类的类, 称为基类
        //swift并没有像OC、Java那样的规定: 任何类最终都要继承自某个基类
    
    /// 子类可以重写父类的下标、方法、属性, 重写必须加上override关键字
    

    3-2、内存结构

    class Animal {
      var age = 0
    }
    class Dog : Animal {
      var weight = 0
    }
    class ErHa : Dog {
      var iq = 0
    }
    
    let a = Animal() //堆空间的对象, 堆空间的内存是16的倍数. 
    a.age = 10
    // 32
    print(Mems.size(ofRef: a))
    /*
    0x00000001000073e0 //这8个字节存放类型信息
    0x0000000000000002 //这8个字节存放引用计数相关信息
    0x000000000000000a //这8个字节存储age
    0x0000000000000000
    */
    print(Mems.meStr(ofRef: a))
    
    let d = Dog()
    d.age = 10
    d.weight = 20
    // 32
    print(Mems.size(ofRef: d))
    /*
    0x0000000100007490 //这8个字节存放类型信息
    0x0000000000000002 //这8个字节存放引用计数相关信息
    0x000000000000000a //这8个字节存储age
    0x0000000000000014 //这8个字节存放weight
    */
    print(Mems.memStr(ofRef: d))
    

    3-3、重写实例方法、下标

    class Animal {
      func speak() {
        print("Animal speak")
      }
      subscript(index: Int) -> Int {
        return index
      }
    }
    
    var anim: Animal
    anim = Animal()
    // Animal speak
    anim.speak()
    // 6
    print(anim[6])
    
    class Cat : Animal {
      override func speak(){
        super.speak()
        print("Cat speak")
      }
      override subscript(index: Int) -> Int {
        return super[index] + 1
      }
    }
    
    anim = Cat()
    // Animal speak
    // Cat speak
    anim.speak()
    // 7
    print(anim[6])
    

    3-4、重写类型方法、下标

    /*
    被class修饰的类型方法、下标, 允许被子类重写
    被static修饰的类型方法、下标, 不允许被子类重写
    */
    class Animal {
      class func speak(){
        print("Animal speak")
      }
      class subscript(index: Int) -> Int{
        return index
      }
    }
    
    class Cat : Animal {
      override func speak(){
        super.speak()
        print("Cat speak")
      }
      override subscript(index: Int) -> Int {
        return super[index] + 1
      }
    }
    

    3-5、重写属性

    /* 
    子类可以将父类的属性(存储、计算)重写为计算属性
    子类不可以将父类属性重写为存储属性
    只能重写var属性, 不能重写let属性
    重写时, 属性名、类型要一致
    子类重写后的属性权限, 不能小于父类属性的权限
        如果父类属性是只读的, 那么子类重写的属性可以是只读的、也可以是可读写的
        如果父类属性是可读写的, 那么子类重写后的属性也必须是可读写的
    */
    class Aimal {
      var age = 0 //存储属性
    }
    class Dog : Animal {
      override var age = 0 //报错, 不允许
      var weight = 0
    } 
    
    /// 重写实例属性
    class Circle {
      var radius: Int = 0.0 //直径 存储属性
      var diameter: Int { //半径 计算属性
        set {
          print("Circle setDiameter")
          radius = newValue / 2
        }
        get {
          pirnt("Cricle getDiameter")
          return = radius * 2
        }
      }
    }
    
    var circle: Circle
    circle = Circle()
    circle.radius = 6
    // Circle getDiameter
    // 12
    print(circle.diameter)
    // Circle setDiameter
    circle.diameter = 20
    // 10
    print(circle.radius)
    
    class SunCircle : Circle {
      override var radius: Int {//从父类继承过来的存储属性都是有存储空间的
        set {
          print("SubCircle setRadius")
          super.radius = newValue > 0 ? newVaule : 0
        }
        get {
          print("SubCricle getRadius")
          return super.radius
        }
      }
      override var diameter: Int {
        set {
          print("SubCircle setDiameter")
          super.diameter = newVlue > 0 ? newValue : 0
        }
        get {
          print("SubCircle getDiameter")
          return super.diameter
        }
      }
    }
    
    var circle = SubCircle()
    
    // SubCircle setRadius
    circle.radius = 6
    
    // SubCircle getDiameter
    // Circle getDiameter
    // SubCircle getRaius
    // 12
    print(circle.diameter)
    
    // SubCircle setDiameter
    // Circle setDiameter
    // SubCircle setRadius
    circle.diameter = 20
    
    // SubCircle getRadius
    // 10
    print(circle.radius)
    

    3-6、重写类型属性

    /*被class修饰的计算类型属性, 可以被子类重写; 存储属性不可以.
    被static修饰的类型属性(计算、存储),不可以被子类重写
    */
    class Circle {
      static var radius: Int = 0
      class var diameter: Int {
        set {
          print("Circle setDiameter")
          radius = newValue / 2
        }
        get {
            print("Circle getDiameter") 
          return radius * 2
        }
      }
    }
    
    class SubCircle : Circle {
      override static var diameter: Int {
        set {
          print("SubCircle setDiameter")
          super.diameter = newValue > 0 ? newValue : 0
        }
        get {
          print("SubCircle getDiameter")
          return super.diameter
        }
      }
    }
    

    3-7、属性观察器

    /// 可以在子类中为父类属性(除了只读计算属性、let属性)增加属性观察器
    class Circle {
      var radius: Int = 1
    }
    class SubCircle: Circle {
      override var radius: Int {//此处不是重写计算属性, 只是添加属性观察器
        willSet {
          print("SubCircle willSetRadius", newValue)
        }
        didSet {
          print("SubCircle didSetRadius", oldValue, radius)//访问radius的存储属性
        }
      }
    }
    var circle = SubCircle()
    // SubCircle willSetRadius 10
    // SubCircle didSetRadius 1 10
    circle.radius = 10
    
    
    class Circle {
      var radius: Int = 1 {//存储属性
        willSet {
          print("Circle willSetRadius", newValue)
        }
        didSet {
          print("Circle didSetRadius", oldValue, radius)
        }
      }
    }
    class SubCircle: Circle {
      override var radius: Int {
        willSet {
          print("SubCircle willSetRadius", newValue)
        }
        didSet {
          print("SubCircle didSetRadius", oldValue, radius)
        }
      }
    }
    var circle = SubCircle()
    // SubCricle willSetRadius 10
    // Circle willSetRadius 10
    // Circle didSetRadius 1 10
    // SubCricle didSetRadius 1 10
    circle.radius = 10
    
    
    class Circle {
      var radius: Int {// 实例计算属性
        set {
          print("Circle setRadius", newValue)
        }
        get {
          print("Circle getRadius")
          return 20
        }
      }
    }
    class SubCircle: Circle {
      override var radius: Int {
        willSet {
          print("SubCircle willSetRadius", newValue)
        }
        didSet {
          print("SubCircle didSetRadius", oldValue, radius)
        }
      }
    }
    
    
    class Circle {
      class var radius: Int {// 类型计算属性
        set {
          print("Circle setRadius", newValue)
        }
        get {
          print("Circle getRadius")
          return 20
        }
      }
    }
    class SubCircle: Circle {
      override static var radius: Int {
        willSet {
          print("SubCircle willSetRadius", newValue)
        }
        didSet {
          print("SubCircle didSetRadius", oldValue, radius)
        }
      }
    }
    // Circle getRadius
    // SubCircle willSetRadius 10
    // Circle setRadius 10
    // Circle getRadius
    // SubCircle didSetRadius 20 20
    SubCircle.radius = 10
    

    第十章 汇编分析多态原理、初始化、可选链

    1-1、多态的实现原理

    /*多态的实现原理: 
    1. OC: Runtime
    2. C++: 虚表(虚函数表)
    
    Swift中没有runtime, Swift的实现跟虚表有点像, Swift中多态的实现原理
    父类指针指向子类对象
    */
    class Animal {
      func speak() {
        print("Animal speak")
      }
      func eat() {
        print("Animal eat")
      }
      func sleep() {
        print("Animal sleep")
      }
    }
    class Dog : Animal {
      func speak() {
        print("Dog speak")
      }
      func eat() {
        print("Dog eat")
      }
      func run() {
        print("Dog run")
      }
    }
    
    var anim: Animal
    anim = Animal()
    animal.speak()
    animal.eat()
    animal.sleep()
    
    anim = Dog()//父类指针指向子类对象
    anim.speak()
    anim.eat()
    anim.sleep()
    //anim.run()
    

    1-2、多态的实现原理

    /*Swift中多态的使用原理:
    类似于C++的虚表,直接将这个对象将来要调用的函数内存地址,提前放到类型信息那里, 这些类型信息肯定是编译完就可以确定的调用谁,程序运行过程中就去那块内存中找就可以了
    */
    

    2-1、初始化

    /*
    类、结构体、枚举都可以定义初始化器.
    类有2种初始化器: 指定初始化器(designated Initializer)、便捷初始化器(convenience Initializer).
    
    每个类至少有一个指定初始化器, 指定初始化器是类的主要初始化器.
    默认初始化器总是类的指定初始化器.
    类偏向于少量指定初始化器, 一个类通常只有一个指定初始化器.
    
    初始化器的相互调用规则:
        指定初始化器必须从它的直系父类调用指定初始化器;
        便捷初始化器必须从相同的类里调用另一个初始化器;
        便捷初始化器最终必须调用一个指定初始化器.
    */
    
    /// 指定初始化器
    init(parameters) {
        statements
    }
    
    /// 便捷初始化器
    convenience init(parameters) {
        statements
    }
    
    class Size {
      // init(){ } 默认初始化器
      var width: Int = 0
      var height: Int = 0
      init(width: Int, height: Int){// 指定初始化器(主要初始化器)
        self.width = width
        self.height = height
      }
      convenience init(width: Int, height: Int){// 便捷初始化器
        self.init()
        self.width = width
        self.height = height
      }
    }
    var s = Size(width: 10, height: 20)//调用初始化器
    

    2-2、便捷初始化器最终必须调用一个指定初始化器

    class Size {
      var width: Int
      var height: Int
      init(width: Int, height: Int) {// 指定初始化器(主要初始化器)
        self.width = width
        self.height = height
      }
      convenience init(width: Int) {
        self.init(width: width, height: 0)
      }
      convenience init(height: Int) {
        self.init(width: 0, height: height)
      }
      convenience init() {
        self.init(width: 0, height: 0)
      }
    }
    var s1 = Size(width: 10, height: 20)
    var s2 = Size(width: 10)
    var s3 = Size(height: 10)
    var s4 = Size()
    

    2-3、指定初始化器必须从它的直系父类调用指定初始化器

    class Person {
      var age: Int
      init(age: Int) {
        self.age = age
      }
      convenience init() {
        self.init(age: 0)
      }
    }
    class Student : Person {
      var score: Int
      init(age: Int, score: Int) {//自己的指定初始化器, 必须指定父类的指定初始化器
        // 第一阶段: 初始化所有存储属性
        self.score = score//必须放在调用父类指定初始化器之前
        super.init(age: age)
        // 第二阶段:实例个性化定制
        self.age = age
      }
      convenience init() {//自己的便捷初始化器, 必须指定自己的指定初始化器
        self.init(age: 0, score: 0)
      }
      convenience init(score: Int) {//自己的便捷初始化器, 必须指定自己的指定初始化器
        self.init(age: 0, score: score)
      }
    }
    

    2-4、两段式初始化

    /*
    Swift在编码安全方面是煞费苦心, 为了保证初始化过程的安全, 设定了两段式初始化、安全检查
    
    两段式初始化:
        第1阶段: 初始化所有存储属性
            ① 外层调用指定/便捷初始化器
            ② 分配内存给实例, 但未初始化
            ③ 指定初始化器确保当前类定义的存储属性都初始化
            ④ 指定初始化器调用父类的初始化器, 不断向上调用, 形成初始化器链
    
        第2阶段: 设置新的存储属性值
            ① 从顶部初始化器往下, 链中的每一个指定初始化器都有机会进一步定制实例
            ② 初始化器现在能够使用self(访问、修改它属性, 调用它的实例方法等等)
            ③ 最终,链中任何便捷初始化器都有机会定制实例以及使用self
    
    安全检查: 
        指定初始化器必须保证在调用父类初始化器之前, 其所在类定义的所有存储属性都要初始化完成;
        指定初始化器必须调用父类初始化器, 然后才能成为继承的属性设置新值;
        便捷初始化器必须先调用同类中的其它初始化器, 然后再为任意属性设置新值;
        初始化器在第1阶段初始化完成之前, 不能调用任何实例方法、不能读取任何实例属性的值,也不能引用self;
        直到第1阶段结束, 实例才算完全合法.
    */
    

    2-5、重写父类的指定初始化器

    /// 当重写父类的指定初始化器时,必须加上override(即使子类的实现是便捷初始化器)
    class Person {
      var age: Int
      init(age: Int) {
        self.age = age
      }
    }
    class Student : Person {
      var score: Int
      init(age: Int, score: Int) {
        self.score = score
        super.init(age: age)
        self.age = age
      }
     // override init(age: Int) { //重写为指定初始化器
     //   self.score = 0 //把自己的初始化完成,再调用父类指定初始化器
     //   self.init(age: age)
     // }
      override convenience init(age: Int) {//重写为便捷初始化器
        self.init(age: age, score:0)//调用自己的指定初始化器
        score = 15
      }
    }
    
    /// 如果子类写了一个匹配父类便捷初始化器的初始化器, 不用加上override
        // 因为父类的便捷初始化器永远不会通过子类直接调用, 因此, 严格来说, 子类无法重写父类的便捷初始化器
    class Person {
      var age: Int
      init(age: Int) {
        self.age = age
      }
      convenience init() {
        self.init(age: 0)
      }
    }
    class Student : Person {
      var score: Int
      init(age: Int, score: Int) {
        self.score = score
        super.init(age: age)
        self.age = age
      }
        init() {//此处不算重写, 不用加override
        self.score = 0
        super.init(age: 0)
      }
      convenience init() {
        self.init(age: 0, score: 0)
      }
    }
    

    2-5、自动继承

    /*
    ① 如果子类没有自定义任何指定初始化器, 它会自动继承父类所有的指定初始化器
    
    ② 如果子类提供了父类所有指定初始化器的实现(要么通过方式①继承,要么重写)
            子类自动继承所有的父类便捷初始化器
    
    ③ 就算子类添加了更多的便捷初始化器, 这些规则仍然适用
    
    ④ 子类以便捷初始化器的形式重写父类的指定初始化器, 也可以作为满足规则②的一部分
    */
    
    class Person {
      var age: Int
      var name: String
      init(age: Int, name: String) {
        self.age = age
        self.name = name
      }
      init() {
        self.age = 0
        self.name = 0
      }
      convenience init(age: Int) {
        self.init(age: age, name: "")
      }
      convenience init(name: String) {
        self.init(age: 0, name: name)
      }
    }
    
    class Student : Person {
      var no: Int = 0
      convenience init(no: Int) {
        self.init()
      }
    }
    // 只要我们自定义了指定初始化器, 那么父类的指定初始化器就不能继承下来.
    

    第十一章 init、deinit、可选链、协议、元类型

    1-1、required

    /*
            用required修饰指定初始化器, 表明其所有子类都必须实现该初始化器(通过继承或重写实现)
            如果子类重写required初始化器时也必须加上required, 不用加override
    */
    class Preson {
      required init() { }
      init(age: Int)
    }
    class Student: Preson {
      required init() {
        super.init()
      }
    }
    

    1-2、属性观察器

    /*
        父类的属性在它自己的初始化器中赋值不会触发属性观察器,但在子类的初始化器中赋值会触发属性观察器
    */
    class Preson {
      var age: Int {
        willSet {
          print("willSet", newValue)
        }
        didSet {
          print("didSet", oldValue, age)
        }
      }
      init() {
        self.age = 0
      }
    }
    
    class Student: Person {
      override init() {
        super.init()
        self.age = 1
      }
    }
    
    // willSet 1
    // didSet 0 1
    var stu = Student()
    

    1-3、可失败初始化器

    // 类、结构体、枚举都可以使用init?定义可失败初始化器
    class Person {
        var name: String
      init?(name: String){
        if name.isEmpty {
          return nil
        }
        self.name = name
      }
    }
    
    //之前接触过的可失败初始化器
    var num = Int("123")
    public init?(_description: String)// Int类型初始化源码
    
    enum Answer : Int {
        vase wrong, right
    }
    var an = Answer(rawValue: 1) //枚举的初始化器
    
    /*
        不允许同时定义参数标签、参数个数、参数类型相同的可失败初始化器和非可失败初始化器
        可以用init!定义隐式解包的可失败初始化器
        可失败初始化器可调用非可失败初始化器, 非可失败初始化器调用可失败初始化器需要进行解包
        如果初始化器调用一个可失败初始化器导致初始化失败, 那么整个初始化过程都失败, 并且之后的代码都停止执行
        可以用一个非可失败的初始化器重写一个可失败初始化器, 但反过来是不行的
    */
    class Person {
        var name: String
      /*
      init!(name: String){//定义隐式解包的可失败初始化器
        if name.isEmpty {
          return nil
        }
        self.name = name
      }
      convenience init() {
        self.init(name: "")
      }
      */
      /*
      init?(name: String){
        if name.isEmpty {
          return nil
        }
        self.name = name
      }
      convenience init() {
        self.init(name: "")!//需强制解包
      }
      */
      init?(name: String){
        if name.isEmpty {
          return nil
        }
        self.name = name
      }
      convenience init?() {
        self.init(name: "")
        self.name = "Jack"
        // ....
      }
    }
    class Student : Person {
      override init(name: String){
        // ....
      }
    }
    
    var p1 = Person(name: "")
    print(p1)
    var p2 = Person(name: "Jack")
    print(p2)
    

    2-1、反初始化器(deinit)

    /*
    deinit叫做反初始化器, 类似于C++的析构函数、OC中的dealloc方法
        当类的实例对象被释放内存时,就会调用实例对象的deinit方法
    */
    class Person {
      deinit {
        print("Preson对象销毁了")
      }
    }
    class Student : Person {
        deinit {
        print("Student对象销毁了")
      }
    }
    var stu: Student? = Student() //ARC
    stu = nil
    /*deinit不接受任何参数, 不能写小括号, 不能自行调用
        父类的deinit能被子类继承
        子类的deinit实现执行完毕后会调用父类的deinit
    */
    

    3-1、可选链 ( Optional Chaining )

    class Car { var price = 0 }
    class Dog { var weight = 0 }
    class Person {
      var name: String = ""
      var dog: Dog = Dog()
      var car: Car? = Car()
      func age() -> Int { 18 }
      func eat() { print("Person eat") }
      subscript(index: Int) -> Int { index }
    }
    
    var person: Person? = Person()
    var age1 = person!.age() // Int
    var age2 = person?.age() // Int?
    var name = person?.name // String?
    var index = person?[6] //Int?
    
    func getName() -> String { "jack" }
    // 如果person是nil, 不会调用getName()
    person?.name = getName()
    
    /*
    如果可选项为nil, 调用方法、下标、属性失败, 结果为nil
    如果可选项不为nil, 调用方法、下标、属性成功, 结果会被包装成可选项
        如果结果本来就是可选项, 不会进行再包装
    */
    if let _ = person?.eat() {// ()?
      print("eat调用成功")
    } else {
        print("eat调用失败")
    }
    
    var dog = person?.dog //Dog?
    var weigth =  person?.dog.weight // Int?
    var price = person?.car?.price //Int?
    /*多个? 可以链接在一起
        如果链中任何一个节点是nil, 那么整个链就会调用失败
    */
    

    3-2、可选链

    var scores = ["Jack": [86, 82, 84], "Rose": [79, 94, 81]]
    scorces["Jack"]?[0] = 100
    scorces["Rose"]?[2] += 10
    scorces["Kate"]?[0] = 88
    
    var num1: Int? = 5
    num1? = 10 // Optional(10)
    
    var num2: Int? = nil
    num2? = 10 // nil
    
    var dict: [String : (Int, Int) -> Int] = [//字符串作为key,函数作为value
      "sum" : (+), // (+)这是语法糖,表示两个Int相加,返回Int类型
      "difference" : (-)// (-)这是语法糖,表示两个Int相减,返回Int类型
    ]
    var result = dict["sum"]?(10, 20) // Optional(30), Int?
    

    4-1、协议 ( Protocol )

    /*协议可以用来定义方法、属性、下标的声明, 协议可以被枚举、结构体、类遵守(多个协议之间用逗号隔开)*/
    Protocol Drawable {
      func draw()
      var x: Int { get set }
      var y: Int { get }
      subscript(index: Int) -> Int { get set}
    }
    
    protocol Test1 { }
    protocol Test2 { }
    protocol Test3 { }
    class TestClass : Test1, Test2, Test3 { }
    
    /*
        协议中定义方法时不能有默认参数值
        默认情况下, 协议中定义的内容必须全部实现
            也有办法办到只实现部分内容, 后面会谈到
    */
    

    4-2、协议中的属性

    /*
        协议中定义属性必须用var关键字
        实现协议的属性权限要不小于协议中定义的属性权限
            协议定义get、set, 用var存储属性或get、set计算属性去实现
            协议定义get, 用任何属性都可以实现
    */
    protocol Drawable {
      func draw()
      var x: Int { get set }
      var y: Int { get }
      subscript(index: Int) -> Int { get set }
    }
    
    class Person : Drawable{
      var x: Int = 0 // 通过存储属性实现协议
      let y: Int = 0
      func draw() {
        print("Person draw")
      }
      subscript(index: Int) -> Int {
        set { }
        get { index }
      }
    }
    
    class Person : Drawable{
      var x: Int {// 通过计算属性实现协议
        get { 0 }
        set { }
      }
      var y: Int { 0 }
      func draw() { print("Person draw") }
      subscript(index: Int) -> Int {
        set { }
        get { index }
      }
    }
    

    4-3、static、class

    /*为了保证通用, 协议中必须用static定义类型方法、类型属性、类型下标*/
    protocol Drawable {
      static func draw()
    }
    
    class Person1: Drawable {
      class func draw() {
        print("Person1 draw")
      }
    }
    
    class Person2: Drawable {
      static func draw() {
        print("Person2 draw")
      }
    }
    

    4-4、mutating

    /*只有将协议中的实例方法标记为mutating
            才允许结构体、枚举的具体实现修改自身内存
            类在实现方法时不用加mutating, 枚举、结构体才需要加mutating
    */
    protocol Drawable {
        mutating func draw()
    }
    
    class Size : Drawable {
      var width: Int = 0
      func draw() {
        width = 10
      }
    }
    
    struct Point : Drawable {
      var x: Int = 0
      mutating func draw() {
        x = 10
      }
    }
    

    4-5、init

    /*协议中还可以定义初始化器init
        非final类实现时必须加上required
    */
    protocol Drawable {
        init(x: Int, y: Int) //定义初始化器
    }
    
    class Point : Drawable {// 非final类. 将来可能有子类
      required init(x: Int, y: Int) { } //遵守协议实现方法, required表示它的所有子类都必须遵守这个协议
    }
    
    final class Size : Drawable {// final类, 规定将来没有子类
        init(x: Int, y: Int) { }
    }
    
    /*如果从协议实现的初始化器, 刚好是重写了父类的指定初始化器
        那么这个初始化必须同时加required、override
    */
    protocol Livable {
        init(age: Int)
    }
    
    class Person {
      init (age: Int) { }
    }
    
    class Student : Person, Livable {
      required override init(age: Int) {
        super.init(age: age)
      }
    } 
    

    4-6、required

    /*
        用required修饰指定初始化器, 表明其所有子类都必须实现该初始化器(通过继承或重写实现)
        如果子类重写了required初始化器, 也必须加上required, 不用加override
    */
    class Person {
      required init() { }
      init (age: Int) { }
    }
    
    class Student : Person {
      required init() {
        super.init()
      }
    } 
    

    4-7、init、init?、init!

    /*
    协议中定义的init?、init!, 可以用init、init?、init!去实现
    协议中定义的init, 可以用init、init!去实现
    */
    protocol Livable {
        init()
      init?(age: Int)
      init!(no: Int)
    }
    
    class Person : Livable {
      required init() { }
      // reuqired init!() { }
      
      required init?(age: Int) { }
      // required init!(age: Int) { }
      // required init(age: Int) { }
      
      required init!(no: Int) { }
      // required init!(no: Int) { }
      // required init(no: Int) { }
    }
    

    4-8、协议的继承

    /*一个协议可以继承其他协议*/
    protocol Runnable {
        func run()
    }
    
    protocol Livable : Runnable {
      func breath()
    }
    
    class Person : Livable {
      func breath() { }
      func run() { }
    }
    

    4-9、协议组合

    /*协议组合, 可以包含1个类类型(最多1个), 枚举和结构体则不能存在其中*/
    protocol Livable { }
    protocol Runnable { }
    class Person { }
    
    // 接收Person或者其子类的实例
    func fn0(obj: Person) { }
    // 接收遵守Livable协议的实例
    func fn1(obj: Livable) { }
    // 接收同时遵守Livable、Runnable协议的实例
    func fn2(obj: Livable & Runnable) { }
    // 接收同时遵守Livable、Runnable协议, 并且是Person或其子类的实例
    func fn3(obj: Person & Livable & Runnable) { }
    
    typealias RealPerson = Person & Livable & Runnable
    // 接收同时遵守Livable、Runnable协议、并且是Person或者其子类的实例
    func fn4(obj: RealPerson) { }
    

    4-10、CaseIterable

    /*让枚举遵守CaseIterable协议, 可以实现遍历枚举值*/
    enum Season : CaseIterable {
      case spring, summer, autumn, winter
    } 
    let seasons = Season.allCases
    print(seasons.count) // 4
    for season in seasons {
      print(season)
    }// spring summer autumn winter
    

    4-11、CustomStringConvertible

    /*遵守CustomStringConvertible协议, 可以自定义实例的打印字符串*/
    class Person : CustomStringConvertible {
      var age: Int
      var name: String
      init(age: Int, name: String){
        self.age = age
        self.name = name
      }
      var description: String { //只读的计算属性
        "age=\(age), name=\(name)"
      }
    }
    var p = Person(age: 10, name: "Jack")
    print(p) //age = 10, name=Jack
    

    5-1、Any、AnyObject

    /*Swift提供了2种特殊的类型: Any、AnyObject
        Any : 可以代表任意类型 (枚举、结构体、类、也包括函数类型)
        AnyObject: 可以代表任意类类型 (在协议后面写上: AnyObject代表只有类能遵守这个协议)
    */
    protocol Runnable: AnyObject { }//AnyObject代表类类型的实例, 这样写代表只有类才能遵守这个协议
    class Person : Runnable { }
    struct Size : Runnable { }// 报错
    
    var stu: Any = 10
    stu = "Jack"
    stu = Student()
    
    //创建1个能存放任意类型的数组
    // var data = Array<Any>()
    var data = [Any]()
    data.append(1)
    data.append(3.14)
    data.append(Student())
    data.append("Jack")
    data.append({ 10 })
    

    6-1、is、as?、as!、as

    /*is用来判断是否为某种类型, as用来做强制类型转换*/
    protocol Runnable { func run() }
    class Person { }
    class Student : Person, Runnable {
      func run() {
        print("Student run")
      }
      func study() {
        print("Student study")
      }
    }
    
    var stu: Any = 10
    print(stu is Int) // true
    stu = "Jack"
    print(stu is String) //true
    stu = Student()
    print(stu is Person) //true
    print(stu is Student) //true
    print(stu is Runnable) //true
    
    var stu: Any = 10
    (stu as? Student)?.study() //没有调用study
    stu = Student()
    (stu as? Student)?.study() // Student study
    (stu as! Student).study() // Student study
    (stu as? Runnable)?.run() // Student run
    
    var data = [Any]()
    data.append(Int("123") as Any)
    print(data.count)
    
    var d = 10 as Double
    print(d) // 10.0
    

    7-1、 X.self、 X.Type、AnyClass

    /*
        X.self是一个元类型(metadata)指针, metadata存放着类型相关信息;
        X.self属性X.Type类型;
    */
    class Person{
      
    }
    var p:Person = Person()
    var pType: Person.Type = Person.self // 元类型指针(取出Person堆空间的前8个字节出来, 指向Person元类型的地址值)
    
    var anyType: AnyObject.Type = Person.self
    anyType = Student.self
    
    public typealias AnyClass = AnyObject.Type
    var anyType2: AnyClass = Person.self
    anyType2 = Student.self
    
    var per = Person()
    //type(of: x) 接收一个实例,能告诉你这个实例的类型是什么
    var perType = type(of: per) // Person.self
    print(Person.self == type(of: per)) // true
    

    7-2、元类型的应用

    class Animal { required init() { } }// 必须使用 required 修饰,确保子类一定有init
    class Cat : Animal { }
    class Dog : Animal { }
    class Pig : Animal { }
    
    func create(_ clses: [Animal.Type]) -> [Animal] {
      var arr = [Animal]()
      for cls in clses {
        arr.append(cls.init()) //cls.init()调用初始化方法,返回实例
      }
      return arr
    }
    print(create([Cat.self, Dog.self, Pig.self]))
    
    /*比如:
    TabBarContorller 
    1: HomeVIewController
    2: AboutViewController
    
    // oc
    NSArray *array = @[HomeViewController.class, AboutViewController.class];
    for(cls in array){
        [[cls alloc]init];
    }
    //swift
    var array = @[HomeViewController.self, AboutViewController.self];
    ....
    */
    
    import Foundation 
    class Person {
        var age: Int = 0
    }
    class Student : Person {
        var no: Int = 0
    }
    //runtime: class_getInstanceSize获取类创建出实例对象需要多少内存
    print(class_getInstanceSize(Student.self)) // 32
    print(class_getSuperclass(Student.self)!) //Person
    print(class_getSuperclass(Person.self)!) //Swift._SwiftObject
    /*从结果可以看得出,Swift还有个隐藏的基类: Swift._SwiftObject
        可以参考Swift源码: https://github.com/apple/swift/blob/master/stdlib/public/runtime/SwiftObject.h
    */
    /*有些runtime的函数还是可以用在纯Swift中*/
    

    7-3、Self

    /*Self一般用作返回值类型, 限定返回值跟方法调用者必须是同一类型(也可以作为参数类型)*/
    protocol Runnable {
      func test () -> self
    }
    class Person : Runnable {
        required init() { }
      func test() -> Self { type(of: self).init() }
    }
    class Student : Person { }
    
    /*如果Self用在类中, 要求返回时调用的初始化器是required的*/
    
    var p = Person()
    // Person
    print(p.test())
    
    var stu = Student()
    // Student
    print(stu.test())
    

    7-4、Person和Person.self

    class Person {
      static var age = 0 //类型属性类型方法, 用类去调用
      static func run() {}
    }
    Person.age = 10 //Person代表一个类名
    Person.run()
    
    Person.self.age = 10//等同于上面
    Person.self.run()// Person.self代表元类型指针
    
    /// 下面的实例方法效果都是一样的
    var p0 = Person()//init()
    var p01 = type(of: p0).init()
    var p1 = Person.self()//init()
    var p2 = Person.init()//init()
    var p3 = Person.self.init()//init()
    
    // var pType0 = Person
    var pType1 = Person.self
    
    /*AnyClass相当于AnyObject.Type*/
    func test(_ cls: AnyClass) {
      
    }
    test(Person.self)
    
    /*总结:
    共同点: 都能去访问类型属性和类型方法
    区别: 一个叫元类类型, 类型不同
    */
    

    相关文章

      网友评论

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

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