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 手动输入内存地址来查看内存布局,如下
- 分析: 用来窥探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 仅仅是为了区分 成员变量使用的