Swift5.0 - day2-流程控制、函数、枚举

一、流程控制

  • 1.1、if 语句

    let age = 5
    if age > 10  {
        print("\(age) 大于 10 ")
    } else if  age > 3 {
        print("\(age) 大于 3 ")
    } else {
        print("\(age) 小于 3 ")
    }
    

    提示:

    • if 后面的条件可以省略小括号,而条件后面的花括号不可以省略
    • if 后面的类型只能是 bool 类型


      if 后面的类型只能是 bool 类型
  • 1.2、while 语句(先判断后执行)

    var num = 5
    while num > 0 {
         num -= 1
         print("num=\(num)")
    }
    

    打印结果:num=4、num=3、num=2、num=1、num=0

  • 1.3、repeat while 语句(先执行后判断)

    var num = -1
    repeat{
        print("num=\(num)")
    }while num > 0
    

    打印结果:num=-1,

    提示:repeat-while 相当于 OC 里面的 do-while

  • 1.4、for 语句

    • 闭区间运算符:a...ba<= 取值 <=b

      let names = ["小1","小2","小3","小4"]
      for i in 0...3 {
           print("name=\(names[i])")
      }
      

      提示:上面的 0...3 可以替换为 let range = 0...3

      let a =  0
      var b = 3
      
      let names = ["小1","小2","小3","小4"]
      for i in a...b {
          print("name=\(names[i])")
      }
      

      提示:区间可以用常量或者变量来表示的

      for _ in 0...3 {
          print("😆")
      }
      

      打印了 四次 😆

    • for 循环里面的 i 默认是 let,有需要的时候我们可以声明为 var,如下

      for var i in 1...3 {
          i += 2
          print("i=\(i)")
      }
      

      打印结果:3、4、5

    • 半开区间运算符:a..<ba <= 取值 < b

      for i in 1..<3 {
      
         print("i=\(i)")
      }
      

      打印结果:1、2

  • 1.5、for - 区间运算符用在数组上

    • 例一

      let names = ["小1","小2","小3","小4"]
      for name in names[0...3] {
           print("name=\(name)")
      }
      

      打印结果:小1、小2、小3、小4

    • 例二:单侧区间:让区间朝一个方向尽可能的远

      let names = ["小1","小2","小3","小4"]
      for name in names[1...] {
          print("name=\(name)")
      }
      

      打印结果:小2、小3、小4

      let names = ["小1","小2","小3","小4"]
      for name in names[...2] {
          print("name=\(name)")
      }
      

      打印结果:小1、小2、小3

      let names = ["小1","小2","小3","小4"]
      for name in names[..<2] {
          print("name=\(name)")
      }
      

      打印结果:小1、小2

      let range = ...5
      print(range.contains(8))
      print(range.contains(3))
      print(range.contains(-1))
      

      打印结果:false、true、true

      提示: let range = ...5 代表 range的范围从无穷小到 5,包含5

  • 1.6、区间类型(重点)

    let range1:ClosedRange<Int> = 1...3
    let range2:Range<Int> = 1..<3
    let range3:PartialRangeThrough<Int> = ...5
    
    • 字符、字符串也能进行区间运算,但默认不能在 for-in 中,因为 ClosedRange:不可数的闭区间

      let stringRange1 = "cc"..."ff" // ClosedRange<String>
      stringRange1.contains("cb")   // false
      stringRange1.contains("dz")   // true 
      stringRange1.contains("fg")   // false
      
      let stringRange2 = "a"..."f"  // ClosedRange<String>
      stringRange2.contains("d")    // true
      stringRange2.contains("n")    // false
      

      提示:比较的是ascii,第1位比较第1位,第2位比较第2位,如果第1位大于第1位就没必要比较第二位了,依次类推


      Ascii字符表
    • 0到~包含了所有可能要用到的 ASCII 字符

      let characterRange:ClosedRange<Character> = "\0"..."~"
      characterRange.contains("b")   // true
      
  • 1.8、带间隔的区间值

    let hours = 12
    let hourInterval = 2
    // 从tickMark 的取值:从 4 开始,累加2,不超过 12
    for tickMark in stride(from: 4, to: hours, by: hourInterval) {
        print("tickMark=\(tickMark)")
    }
    

    打印结果:4、6、8、10

  • 1.9、switch 语句

    • 案例一:switch的基本使用

      var num = 10
      switch num {
      case 1:
           print("值是1")
           break
      case 2:
           print("值是2")
           break
      case 3:
           print("值是3")
           break
      default:
           print("没有匹配")
           break
      }
      

      提示:case 和 default 后面不能写大括号 {},默认可以不写 break,并不会贯穿到后面的条件

    • 案例二:使用 fallthrough 可以实现贯穿效果

      var num = 1
      switch num {
      case 1:
         print("num 值是 1")
         fallthrough
      case 2:
         print("num 值是 2")
      case 3:
         print("num 值是 3")
      default:
         print("没有匹配")
      }
      

      打印结果:num 值是 1、num 值是 2

    • switch使用注意点:

      • <1>、switch 必须能保证处理所有的情况


        switch 必须能保证处理所有的情况
      • <2>、case 或者 default 后面至少有一条语句,如果不想有语句可以写 break

        var num = 1
        switch num {
        case 1:
            print("num 值是 1")
        case 2:
            break
        default:
            print("没有匹配")
        }
        
      • <3>、如果能保证处理所有的情况,也可以不必使用 default,如下

        enum Answer {case right,wrong}
        let answer = Answer.right
        switch answer {
        case Answer.right:
            print("right")
        case Answer.wrong:
            print("wrong")
        }
        

        提示:上面已经确定 answer 是 Answer 类型,因此可以省略 Answer

        enum Answer {case right,wrong}
        let answer = Answer.right
        switch answer {
        case .right:
             print("right")
        case .wrong:
             print("wrong")
        }
        
    • 复合条件:switch也支持Character、String 类型,如下例子

      • 例一:String 类型

        let name = "marry"
        switch name {
        case "Jack":
            print("Jack")
        case "marry":
            print("marry")
        default:
            print("找不到某人")
        }
        
      • 例二:String 类型

        let name = "marry"
        switch name {
        case "Jack","marry":
            print("Jack and marry")
        default:
            print("找不到某人")
        }
        
      • 例三:Character 类型

        let character = "A"
        switch character {
        case "a","A":
            print("字母 a或者A")
        default:
            print("找不到字母")
        }
        
    • 匹配区间和元组匹配

      • 匹配区间

        let count = 20
        switch count {
        case 0:
           print("none")
        case 1..<5:
           print("a few")
        case 5..<100:
           print("several")
        case 100..<1000:
           print("hundreds of")
        default:
           print("many")
        }
        
      • 元组匹配

        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("many")
        }
        

        提示:可以使用 _ 忽略某个值

    • 值绑定:必要时,let 可以改为 var

      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("somehere else at (\(x),\(y))")
      }
      
  • 1.10、where语句

    let point = (2,-2)
    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")
    }
    

    例二:所有的正数相加

    let numbers = [-1,2,3,-10,20]
    var sum = 0
    for num in numbers where num > 0 {
    
       sum += num
    }
    print("最后正数的和=\(sum)")
    

    提示:where 用来过滤 负数

  • 1.11、标签语句

    outer: for i in 1...4{
    
       for k in 1...4 {
      
          if k == 3 {
              continue outer
          }
          if i == 3 {
              break outer
          }
          print("i=\(i) k=\(k)")
       }
    }
    

二、函数

  • 2.1、函数的定义:是一组代码的组合,因此函数也被称为完成特殊功能的代码块。函数三要素:函数名、参数、返回值。

    • 有返回值,有参数

      func pi()->Double{
          return 3.14
      }
      print(pi())
      
      func sum(num1:Int,num2:Int)->Int{
          return num1 + num2
      }
      print(sum(num1: 1, num2: 3))
      

      提示:形参默认是 let,也只能是 let

    • 无返回值,无参数;下面的几种 无返回值表达的意思是一样的

      func sayHello()->Void{
         print("Hello")
      }
      
      func sayHello()->(){
         print("Hello")
      }
      
      func sayHello(){
         print("Hello")
      }
      
  • 2.2、函数的隐式返回

    func sum(num1:Int,num2:Int) -> Int{
         num1 + num2
    }
    sum(num1: 1, num2: 3)
    

    提示:如果整个函数的整体是一个单一的表达式,那么函数会隐式返回这个表达式

  • 2.3、返回元组 和 实现多值返回

    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   // 和 
    result.difference  // 差 : 10
    result.average   // 二进制右移 是 平均值 15
    

    提示:sum>>1 代表值的二进制数 右移 求平均值

  • 2.4、函数的文档注释 参考苹果的链接

    /// 求和 【概述】
    ///
    /// 将两个整数相加 【更详细的描述】
    ///
    /// - Parameter v1: 第 1 个整数
    /// - Parameter v2: 第 2 个整数
    /// - Returns : 两个整数的和
    ///
    /// - Note:传入两个整数即可 【批注】,有没有什么特殊的用法
    ///
    func sum(_ v1:Int,_ v2:Int) -> Int{
        return v1 + v2
    }
    
    sum(10, 20)
    

    效果如下:


  • 2.5、参数标签

    • 可以修改参数标签

      func goToWork(at time:String){
      
           print("this time is \(time)")
      }
      
      goToWork(at: "08:00")
      

      打印结果:this time is 08:00

    • 可以使用下划线 _ 省略参数标签

      func sum(_ v1:Int,_ v2:Int) -> Int{
          return v1 + v2
      }
      
      sum(10, 20)
      
  • 2.6、默认参数值(Default Parameter Value)

    • 参数可以有默认值

      func check(name:String = "noPerson",age:Int,job:String = "iOS"){
      
          print("名字=\(name) 年龄=\(age) 工作=\(job)")
      }
      
      check(name: "老大", age: 19, job: "iOS")
      check(name: "老二", age: 19)
      check(age: 10, job: "前端")
      check(age: 16)
      

      提示:只要是有默认参数值的就可以不传参数

      • C++的默认参数值有个限制:必须从右往左设置。由于Swift拥有参数标签,因此并没有此类限制

      • 但是在省略参数标签时,需要特别注意,避免出错,如下
        这里的 two 不可以省略参数标签

        func test(_ one:Int = 10,two:Int,_ three:Int = 20){
        
            print("打印 one = \(one) two = \(two) three = \(three)")
        }
        
        test(two: 9)
        
  • 2.7、可变参数(Variadic Parameter)

    func sum(_ numbers: Int...) -> Int {
    
       var total = 0
       for i in numbers {
           total += I
       }
       return total
    }
    
    sum(1,2,3)
    

    提示:一个函数 最多只能有1个 可变参数;紧跟在可变参数后面的参数不能省略参数标签,如下

    func test(_ numbers:Int...,string:String,_ other:String){
    
    }
    
    test(1,2,3, string: "iOS", "Rose")
    

    参数 string 标签不能省略

  • 2.8、Swift 自带的 print 函数

    /// - Parameters:
    ///   - items: Zero or more items to print.
    ///   - separator: A string to print between each item. The default is a single
    ///     space (`" "`).
    ///   - terminator: The string to print after all items have been printed. The
    ///     default is a newline (`"\n"`).
    public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
    

    分析: 第一个参数是要打印的值,第二个参数是:连接第一个参数值的 字符,第三个参数是:默认是 \n 换行,如下例子

    print(1,2,3,4,5)
    print(1,2,3, separator: "_")
    print(1,2,3, separator: "_", terminator: "")
    print("哈哈")
    

    打印结果是: 最后一行不换行,因为 "\n" 被用 "" 取代了

    1 2 3 4 5
    1_2_3
    1_2_3哈哈
    
  • 2.9、输入输出参数 (In-Out Parameter)
    可以用 inout 定义一个输入输出函数:可以在函数内部修改外部实参的值

    func swapValues(_ v1:inout Int,_ v2:inout Int){
    
        let tem = v1
        v1 = v2
        v2 = tem
    }
    
    var num1 = 10
    var num2 = 20
    swapValues(&num1, &num2)
    
    print(num1,num2)
    

    打印结果:20 10

    • 提示:下面的函数与上面的等价:传进去的是 内存地址,所以可以修改其外部参数的值

      func swapValues(_ v1:inout Int,_ v2:inout Int){
      
         (v1,v2) = (v2,v1)
      }
      
    • 可变参数不能标记为 inout

    • 输入输出参数不能有默认值

    • 输入输出参数不能传入常量(let)、字面量作为实参。也就是可以被多次赋值的 变量

    • inout 参数的本质是地址传递 (引用传递)

  • 2.10、函数重载 (Function Overload)
    规则:函数名相同
    参数个数不同 || 参数类型不同 || 参数标签不同

    func sum(v1:Int,v2:Int) -> Int{
       return v1 + v2
    }
    

    参数个数不同

    func sum(v1:Int,v2:Int,v3:Int) -> Int{
        return v1 + v2
    }
    

    参数类型不同

    func sum(v1:Int,v2:Double) -> Double{
       return Double(v1) + v2
    }
    
    func sum(v1:Double,v2:Int) -> Double{
       return v1 + Double(v2)
    }
    

    参数标签不同

    func sum(_ v1:Int,_ v2:Int) -> Int{
       return v1 + v2
    }
    
    func sum(a:Int,b:Int) -> Int{
       return a + b
    }
    
    • 函数重载注意点一: 返回值类型 与 函数重载 无关

      func sum(a:Int,b:Int) -> Int{
          return a + b
      }
      
      func sum(a:Int,b:Int) {
      
      }
      
      sum(a: 1, b: 2)
      
    • 函数重载注意点二:默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(在 C++ 中会报错)

      func sum(a:Int,b:Int) -> Int {
           return a + b
      }
      
      func sum(a:Int,b:Int,c:Int = 10) -> Int {
          return a + b + c
      }
      // 会调用 sum(a:Int,b:Int),这样不太好,还不如编译器报错
      sum(a: 1, b: 2)
      
    • 函数重载注意点三:可变参数,省略参数标签,函数重载一起使用产生二义性时,编译器有可能会报错

      func sum(a:Int,b:Int) -> Int{
          return a + b
      }
      
      func sum(_ a:Int,_ b:Int) -> Int {
          return a + b
      }
      
      func sum(_ numbers: Int...)->Int{
      
          var total = 0
          for i in numbers {
              total += I
          }
          return total
      }
      

      调用 sum(1,2) 报错:Ambiguous use of 'sum'

  • 2.11、内联函数(Inline function)

    • 如果开启了编译器优化(Release模式默认会开启优化),编译器会默认将某些函数变成内联函数
      • 将函数调用展开成函数体

      将函数调用展开成函数体

      不会被内联的函数:函数体比较长、包含递归调用、包含动态派发......

  • 2.12、函数类型:每一个函数都是有类型的,函数的类型由:形式参数类型,返回值类型组成

    • 例子一:无参数无返回值

      func test() {}
      
    • 例子二:有参数有返回值

      func test(a: Int, b: Int) -> Int {
         return a + b
      }
      
    • 例子三:调用的时候不需要传参数标签

      func test(_ a: Int ,_ b: Int) -> Int {
          return a + b
      }
      test(2,3)  // 调用的时候不需要传参数标签
      
  • 2.13、函数类型作为函数参数

    func sum(a: Int, b: Int) -> Int{
        return a + b
    }
    func difference(a:Int,b:Int) -> Int{
        return a - b
    }
    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
    
  • 2.14、函数类型作为函数返回值

    func next(_ input: Int) -> Int {
        return input + 1
    }
    
    func previous(_ input: Int) -> Int {
        return input - 1
    }
    
    func forward(_ forward: Bool) -> (Int) -> Int {
        return forward ? next:previous
    }
    
    print(forward(true)(3))  //  结果:4
    print(forward(false)(3))  //  结果:2
    

    提示:返回值是函数的函数,叫做高阶函数(Height-Order Function)

  • 2.15、typealias: 用来给类型起别名

    typealias Byte = Int8
    typealias Short = Int16
    typealias Long = Int64
    

    如下:

    typealias Date = (year:Int,month:Int,day:Int)
    
    func test(_ date:Date){
    
        print(date.year,date.month,date.day)
    }
    
    test((2019,6,19))
    

    例二:(重点👂👂👂👂👂👂👂👂👂👂)

    typealias IntFn = (Int,Int) -> Int
    
    func difference(v1:Int,v2:Int)->Int{
         return v1-v2
    }
    let fn:IntFn = difference
    
    fn(20,10)
    
    func setFn(_ fn:IntFn){}
    setFn(difference)
    
    func getFn() -> IntFn{
    
        return difference
    }
    
  • 2.16、嵌套函数: 将函数定义在函数的内部

    func forward(_ forward:Bool)->(Int)->Int{
    
        func next(_ input:Int)->Int{
      
             return input + 1
        }
    
        func previous(_ input:Int)->Int{
      
             return input - 1
        }
    
        return forward ? next:previous
    }
    
    forward(true)(3)  // 4
    forward(false)(3) // 2
    

三、枚举

  • 3.1、枚举的基本用法

    enum Direction {
        case north
        case south
        case east
        case west
    }
    
    enum Direction {
        case north,south,east,west
    }
    

    使用如下:

    var dircetion = Direction.north
    dircetion = Direction.west
    dircetion = .south
      
    print("dircetion=\(dircetion)")
    
    let dir = Direction.north
      
    switch dir {
    case .north:
          print("north")
    case .south:
          print("south")
    case .east:
          print("east")
    case .west:
          print("west")
    }
    
  • 3.2、关联值(Associated Values)

    • 有时会将枚举的成员值跟其他类型的关联存储在一起,会非常有用

      enum Score {
          case points(Int)
          case grade(Character) 
      }
      
      var source = Score.points(96)
      source = .grade("A")
      
      switch source {
      case let .points(i):
         print(i,"points")
      case let .grade(i):
        print("grade",i)
      }
      

      打印结果:grade A

    • 下面代码必要的时候 let 可以改为 var

       enum Date{
      
           case digit(year:Int,month:Int,day:Int)
           case string(String)
       }
      
       var date = Date.digit(year:2019,month:6,day:21)
       date = .string("2019-6-21")
      
       switch date {
       case .digit(let year,let month,let day):
            print(year,month,day)
       case let .string(value):
            print(value)
       }
      

      打印结果:2019-6-21

    • 关联值举例


      关联值手势举例
      enum Password{
      
         case number(Int,Int,Int,Int)
         case gesture(String)
      }
      
      var pwd = Password.number(3,5,7,8)
      pwd = .gesture("12368")
      
      switch pwd {
      case let .number(n1,n2,n3,n4):
          print("number is \(n1) \(n2) \(n3) \(n4)")
      case let .gesture(str):
          print("gesture is",str)
      }
      

      打印结果:gesture is 12368

  • 3.3、原始值 (Raw Values):枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值

    enum PlayingCards : Character{
    
        case A = "a"
        case B = "b"
        case C = "c"
        case D = "d"
    }
    
    var suit = PlayingCards.A
    
    print(suit)   // A
    print(suit.rawValue) // a
    print(PlayingCards.D.rawValue) // d
    
    enum 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
    
  • 3.4、隐式原始值 (Implicitly Assignd RawValues):如果枚举的原始类型是Int、String 类型,Swift会自动分配原始值

    • String 类型

      enum Direction {
           case north,south,east,west
      }
      print(Direction. north) // north
      print(Direction. north.rawValue) // north
      

      等价于

      enum Direction: String {
         case north =  "north"
         case south =  "south"
         case east =  "east"
         case west =  "west"
      }
      
    • Int 类型

      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
      
    • Int 类型设置了默认原始值,设置了值之后 Swift会自动递增原始值

      enum Season:Int{
      
         case spring = 2,summer,autumn = 5,winter
      }
      
      print(Season.spring.rawValue)   // 2
      print(Season.summer.rawValue)  // 3
      print(Season.autumn.rawValue)  // 5
      print(Season.winter.rawValue)  // 6
      
  • 3.5、递归枚举 (Recursive Enumeration)

    enmu 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)
    

    提示:使用 indirect 关键字修饰的枚举值表示这个枚举是可以递归的,即此枚举值中的相关值使用其枚举类型本身。

  • 3.7、MemoryLayout: 获取数据类型占用的内存大小

    enum Password{
    
        case number(Int,Int,Int,Int)  // 32 字节
        case other  // 1 个字节就搞定了
    }
    
    MemoryLayout<Password>.stride   // 40 分配占用的内存空间大小
    MemoryLayout<Password>.size  // 33 实际用到的空间大小
    MemoryLayout<Password>.alignment  // 8 对齐参数
    
    var password = Password.number(2,3,4,5)
    password = .other
    
    MemoryLayout.stride(ofValue:password)   // 40
    MemoryLayout.size(ofValue:password)  // 33
    MemoryLayout.alignment(ofValue:password)  // 8
    
    • 提示1:stride:范围 与 size 的区别
      size: 分配占用的内存空间大小
      stride: 实际用到的空间大小
      内存对齐是 8个字节 ,所以上面是 32+1 = 40
    • 提示2:N个字节存储关联值(N取内存最大的关联值),比如number(Int,Int,Int,Int)就是 4*8=32 里面的元素是关联值,1个字节用来存储成员值(也就是case的个数)
  • 3.8、枚举拓展

    • 拓展一:枚举的关联值(3.2) 和 默认原始值(3.3) 的区别
      分析:枚举的关联值 的值 是写到枚举的内存中的,而枚举的原始值是固定死的,没有写入枚举的内存中
      举例如下:

      enum Season:Int{
      
           case spring,summer,autumn,winter
      }
      
      MemoryLayout<Season>.stride   // 1
      MemoryLayout<Season>.size  // 1
      MemoryLayout<Season>.alignment  // 1
      
    • 拓展二:思考下面枚举变量的内存布局

      enum TestNum {
         case test1,test2,test3,test4
      }
      
      print( MemoryLayout<TestNum>.stride) // 1 分配占用的内存空间大小
      print(MemoryLayout<TestNum>.size) // 1 实际用到的空间大小
      print(MemoryLayout<TestNum>.alignment) // 1 对齐参数
      
      • 提示1:直接查看内存布局


        内存布局的展示
      • 提示2:我们还可以在Debug -> Debug Workflow -> View Memory 手动输入内存地址来查看内存布局,如下


        在Debug -> Debug Workflow -> View Memory 手动输入内存地址来查看内存布局

      分析: 用来窥探Swift内存的小工具

      var t = TestNum.test1
      print( Mems.ptr(ofVal: &t))
      t = .test2
      t = .test3
      t = .test4
      
      • 上面是采取一个字节来存储枚举变量的数据,一个字节 00 用来存储 test1、一个字节 01 用来存储 test2、一个字节 02 用来存储 test3、一个字节 03 用来存储 test4,其实也就是来区分成员变量的值
      • 按照上面的一个字节来区分的话,一个字节的范围是 0x00~0xFF,最大也就是 256,每一个case也就是一个成员变量,其实case test1,test2,test3,test4 相当于四个case
    • 拓展三:看下面一个复杂的枚举

      enum TestNum {
        case test1(Int,Int,Int)
        case test2(Int,Int)
        case test3(Int)
        case test4(Bool)
        case test5
      }
      
      print(MemoryLayout<TestNum>.size)  //  25分配的内存大小
      print(MemoryLayout<TestNum>.stride) // 32 实际使用的内存大小
      print(MemoryLayout<TestNum>.alignment)  // 8 内存对齐的字节数
      

      分析:分配内存的规则:如果有多个case,那么必有一个字节存储来区分成员变量case,再看case关联值最多的那个有多少个关联值,N个字节存储关联值(N取内存最大的关联值),比如number(Int,Int,Int)就是 3*8=24 里面的元素是关联值,1个字节用来存储成员值(也就是case的个数),那么分配的内存是 32,实际用了 24+1= 25

    • 拓展四:看下面一个有意的的枚举

      enum TestNum {
          case test1(Int,Int,Bool,Bool)
          case test2(Int,Int)
      }
      
      print(MemoryLayout<TestNum>.size)  //  实际使用的内存大小 18
      print(MemoryLayout<TestNum>.stride) // 分配的内存大小 24
      print(MemoryLayout<TestNum>.alignment) // 内存对齐的字节数 8
      

      分析:8+8+最后一个Bool【1(第一个bool)+1(区别成员变量) 】= 17 这是编译器的进一步优化,它把case标志放在最后一个Bool字节里去了,相当于最后一个Bool,它的那个字节里面包含了case成员信息。因为Bool类型只有2种取值,true是1,false是0,只会用到1个二进制位,所以Bool的1个字节还是有多余空间的,如果是Int这些,就有可能要把它自己的8个字节都用完。所以无法拿来放case,比如0xFF FF FF FF FF FF FF FF,占满了8字节,而Bool,你的8个二进制位,不管存储true还是false,都只需要用到1位

    • 拓展五:没有关联值

      enum TestNum {
           case test1,test2,test3,test4
      }
      
      print(MemoryLayout<TestNum>.size)  //  实际使用的内存大小 1
      print(MemoryLayout<TestNum>.stride) // 分配的内存大小 1
      print(MemoryLayout<TestNum>.alignment) //  内存对齐的字节数 1
      

      分析:这里的 1 仅仅是为了区分 成员变量使用的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容