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

简介: Swift5.0 - day2-流程控制、函数、枚举(下)

2.11、内联函数(Inline function)


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


image.png

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


  • 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
  • 关联值举例


image.png

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:直接查看内存布局


image.png

image.png


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


image.png



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 仅仅是为了区分 成员变量使用的
目录
相关文章
|
5天前
|
存储 Swift
Swift 语言:什么是闭包(Closure)?它们与函数的区别是什么?
Swift 语言:什么是闭包(Closure)?它们与函数的区别是什么?
43 1
|
5天前
|
Swift
Swift中的函数
Swift中的函数
23 1
|
5天前
|
存储 Swift
Swift中,函数和闭包
Swift中,函数和闭包
37 1
|
7月前
|
Swift iOS开发
23 Swift中如何定义和使用函数
Swift中如何定义和使用函数
54 0
|
8月前
|
Swift
Swift Debug 和 Release 中 print() 函数调试切换
Swift Debug 和 Release 中 print() 函数调试切换
45 0
|
Swift C语言
深入浅出Swift(3)—— 函数
深入浅出Swift(3)—— 函数
64 0
|
Swift C语言
【Swift 5.1】流程控制、函数与内联函数优化
文章目录 1.流程控制 1.1 while循环 eg1. 简单的打印例子1
|
缓存 前端开发 Swift
Swift实用小册06:函数的定义、参数、返回、调用
Swift实用小册06:函数的定义、参数、返回、调用
202 0
Swift实用小册06:函数的定义、参数、返回、调用
【Swift4】(5) 函数基本使用 | 可变参数 | inout引用传递 | 函数类型返回值 | 函数嵌套
【Swift4】(5) 函数基本使用 | 可变参数 | inout引用传递 | 函数类型返回值 | 函数嵌套
160 0
|
编译器 Swift C++
Swift5.0 - day2-流程控制、函数、枚举(上)
Swift5.0 - day2-流程控制、函数、枚举(上)
127 0
Swift5.0 - day2-流程控制、函数、枚举(上)