流程控制
if
if
后面的条件可以省略小括号,条件只能是Bool类型
let age = 20
if age > 20 {
print("20多岁了")
}
else if age < 10 {
print("小孩子")
}
else {
print("年轻人")
}
// 以下写法将不会编译通过(if 条件只能是Bool类型)
if age {
}
while
repeat-while
相当于C语言的do while
// while 循环
var num = 5
while num > 0 {
print("num is \(num)")
num -= 1
}
// repeat while 循环
var num2 = -1
repeat {
print("num is \(num2)")
} while num2 > 0
for
for
循环使用区间运算符
闭区间运算符:比如0...3
代表大于等于0小于等于3
let names = ["张三", "李四", "思聪", "尼古拉斯"]
for i in 0...3 {
print(names[i])
}
// 也可以将区间运算符定义为常量
let names = ["张三", "李四", "思聪", "尼古拉斯"]
let range = 0...3
for i in range {
print(names[i])
}
// 也可以区间运算符使用某一个变量\常量
let index = 0
let names = ["张三", "李四", "思聪", "尼古拉斯"]
for i in index...3 {
print(names[i])
}
// i默认是let,也可以声明为var变量
for var i in 1...3 {
i+=5
print(i)
}
// 如果循环语句当中没有用到i,可以写成_ 表示忽略循环变量
for _ in 1...3 {
print("循环了")
}
半开区间运算符:比如a..<b
代表 大于等于a小于b
// 代表 大于等于1小于3
for i in 1..<3 {
print("循环了")
}
条件过滤
区间运算符后边可以用where
或filter
关键字来控制运算符的逻辑
// 使用where完成条件过滤
for i in 0..<10 where i%2==0 {
print(i) //输出结果为1-10之间的偶数
}
// 使用filter完成条件过滤
for i in (1...10).filter({ i in i % 2 == 0}) {
print(i) // 输出结果为1-10之间的偶数
}
反向遍历
使用reverse
关键字让遍历条件反转
for i in (0...10).reverse(){
print(i) // 打印结果为10-0
}
遍历元素以及索引
使用enumerate
关键字可以获得遍历的索引以及遍历的元素
for (index, i) in (1...10).enumerate(){
print(index)// 遍历索引
print(i) // 遍历元素
}
区间运算符应用在数组上:
// 相当于OC当中的for (NSString *name in names)
let names = ["张三", "李四", "思聪", "尼古拉斯"]
for name in names[0...3] {
print(name)
}
单侧区间:让区间向一个方向尽可能的远
// 下面这种写法代表从数组的第二个元素开始,一直到数组的最大长度开始遍历
let names = ["张三", "李四", "思聪", "尼古拉斯"]
for name in names[1...] {
print(name)
}
// 下面这种写法代表从数组的第1个元素开始,一直到数组的第三个元素开始遍历
let names = ["张三", "李四", "思聪", "尼古拉斯"]
for name in names[...2] {
print(name)
}
// 半开区间,小于数组的第三个元素
let names = ["张三", "李四", "思聪", "尼古拉斯"]
for name in names[..<2] {
print(name)
}
// 无穷区间(代表负无穷到小于等于5)
let range = ...5
range.contains(-1) // true
range.contains(6) // false
区间类型
区间类型分为三种
1.ClosedRange
2.Range
3.PartialRangeThrough
ClosedRange
当区间有开闭区间
时,类型为ClosedRange
let range1: ClosedRange<Int> = 1...3
Range
当区间有半开区间
时,类型为Range
let range2: Range<Int> = 1..<3
PartialRangeThrough
当区间只有单侧区间
时,类型为PartialRangeThrough, 如:
let range3: PartialRangeThrough<Int> = ...5
字符、字符串也能使用区间运算符,但默认不能用在for-in
中,如:
// 字符串使用区间运算符(这种写法表示取值范围为:cc,cd,ce,cf....fe,ff)
let stringRange1 = "cc...ff" // 类型为ClosedRange<String>
stringRange1.contains("cb") // false
stringRange1.contains("dz") // true
// 也可以这样写
let stringRange2 = "a...f"
stringRange2.contains("c") // true
stringRange2.contains("z") // flase
// 声明区间类型为CloseRange<Character>, \0到~囊括了所有ASCII字符
let characterRange: CloseRange<Character> = "\0"..."~"
characterRange.contains("G") // true
带间隔的区间值
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
}
switch
case、default后面不能写大括号{}, 也可以不用写break(并不会贯穿到后面的条件)如:
var number = 1
switch number {
case 1:
print("number = 1")
break; // 也可以不用写break,不会贯穿到后面的条件执行
case 2:
print("number = 2")
break; // 也可以不用写break,不会贯穿到后面的条件执行
default:
print("number is other")
break; // 也可以不用写break,不会贯穿到后面的条件执行
}
// 打印结果:number = 1
fallthrough可以实现贯穿效果
var number = 1
switch number {
case 1:
print("number = 1")
fallthrough
case 2:
print("number = 2")
default:
print("number is other")
}
// 打印结果number = 1 number = 2
switch
语句必须要保证能处理所有情况,没有保证能够处理所有情况就会编译失败:
处理方法:case
、default
后面至少要有一条语句;如果不想做任何事情,加一个break
即可
var number = 1
switch number {
case 1:
print("number = 1")
case 2:
print("number = 2")
default:
break
}
如果能够保证处理所有情况(比如枚举值),就不必使用default
,如:
enum answer {case wrong, right}
let result = answer.right
switch result {
case answer.wrong:
print("你的答案错误了")
case answer.right:
print("正确")
}
switch
也支持Character
、String
类型, 如:
let name = "jack"
switch name {
case "jack":
print("jack")
case "robin":
print("robin")
default:
break
}
// 也可以这样写,等同于贯穿执行
let name = "jack"
switch name {
case "jack", "robin":
print("jack,robin")
default:
break
}
区间匹配、元组匹配
switch
支持区间匹配
:
let count = 62
switch count {
case 0:
print("0")
case 1..<5:
print("a few")
case 10..<100:
print("dozens of")
case 100..<1000:
print("hundreds of")
default:
break
}
元组匹配
:
let point = (1, 1)
switch point {
case (0, 0):
print("origin")
case (_, 0): // 代表左边是什么值都行
print("x-axis")
case (0, _): // 代表右边是什么值都行
print("y-axis")
case (-2...2, -2...2):
print("inside the box")
default:
print("outside")
break
}
值绑定
可以当某些条件满足时,可以用来接收另外一些值
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 a y value of \(y)")
case let(x, y):
print("somewhere else at \(x), \(y)")
}
// 输出:on the x-axis with an x value of 2
where
当使用值绑定时,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("just some arbitrary point")
}
// 输出:on the line x==-y
where
不仅仅可以用在switch
里面,还可以用在for
循环里面; 当使用where时会筛选符合条件的对象
var numbers = [10,20,-10,-20,30,-39]
var sum = 0
for num in numbers where num > 0 {
sum += num
}
print(sum) // 输出:60
标签语句
标签语句
:可以在某个条件下控制某个条件
outer: for i in 1...4 {
for k in 1...4 {
if k == 3 {
// 当continue在内层循环下,默认情况下控制的就是内层循环
// 外层循环定义标签,当执行内层循环到达条件k==3时,就控制外层循环
continue outer
}
if i == 3 {
break outer
}
print("i == \(i), k == \(k)")
}
}
// 输出
i == 1, k == 1
i == 1, k == 2
i == 2, k == 1
i == 2, k == 2
函数
函数定义
函数
使用func
来定义
// 方法名:pi 参数:无 返回值类型:double
func pi() -> Double {
return 3.14
}
// 方法名:sum 参数:两个int类型 返回值类型:int
func sum(v1: Int, v2: Int) -> Int {
return v1+v2
}
// 调用方式
sum(v1: 10, v2: 20)
方法的形参默认是
let
类型,也只能是let
类型
无返回值的方法定义:
func sayHello() ->Void {
print("hello")
}
func sayHello2() ->() {
print("hello")
}
func sayHello3() {
print("hello")
}
隐式返回
如果整个函数是一个单一表达式,那么函数会隐式返回这个表达式
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
// 调用函数
sum(v1: 10, v2: 20)
返回值:元组
函数支持返回元组类型,可以实现返回多个值
// 定义函数返回元组,实现计算和、差、平均值
func calculate(v1: Int, v2: Int) -> (sum: Int, differenct: Int, average: Int) {
let sum = v1 + v2
return (sum, v1 - v2, sum >> 1) // 参数相加的和,右移一位(平均值)
}
let result = calculate(v1: 10, v2: 20)
result.sum
result.differenct
result.average
参数标签
可以修改参数标签
func goToWork(at time: String) {
// 使用参数时,用time
print("在\(time)上班了")
}
// 调用方法时,用at
goToWork(at: "9:00")
// 输出:在9:00上班了
忽略参数标签
可以省略调用方法时的参数标签
func sum(_ v1: Int, _ v2: Int) -> Int {
v1 + v2
}
sum(10, 20)
默认参数值
函数支持默认值
func check(name: String = "nobody", age: Int, job: String = "none") {
print("name = \(name), age = \(age), job = \(job)")
}
check(name: "jack", age: 22, job: "docter")
check(name: "rose", age: 18)
check(age: 18, job: "batman")
check(age: 19)
// 输出:
name = jack, age = 22, job = docter
name = rose, age = 18, job = none
name = nobody, age = 18, job = batman
name = nobody, age = 19, job = none
C++
的默认参数值有个限制:必须从右往左设置。由于Swift
拥有参数标签,因此并没有此限制
可变参数
可变参数可用…来表示, 一个函数最多只有一个可变参数
func sum(_ numbers: Int...) ->Int {
var total = 0
for number in numbers {
total += number
}
return total
}
sum(10,20,30,40) // 100
紧跟在可变参数后面的参数不能省略参数标签
// 可变参数后面的参数不能省略参数标签
func test(_ numbers: Int..., string: String, _other: String) {
}
// 调用
test(10,20,30, string: "jack", _other: "Rose")
输入输出参数
用inout
定义一个输入输出参数:可以在函数内部修改外部实参的值
var number = 10
func add(_ num: inout Int) {
num += 1
}
add(&number)
print(number) // 输出:11
可变参数
不能使用输入输出参数
可变参数
的本质是地址传递
可变参数
只能传入可以被多次赋值的
(比如外部实参不能使用let定义)
函数重载
- 函数名相同
- 参数
个数不同
或者参数类型不同
或者参数标签不同
// sum函数有重载------(参数个数不同)
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
func sum(v1: Int, v2: Int, v3: Int) -> Int {
v1 + v2 + v3
}
// sum2函数有重载-------(参数类型不同)
func sum2(v1: Int, v2: Double) -> Int {
Double(v1) + v2
}
func sum2(v1: Double, v2: Int) -> Int {
v1 + Double(v2)
}
// sum3函数有重载-------(参数标签不同)
func sum3(_ v1: Int, _ v2: Int) -> Int {
v1 + v2
}
func sum3(a: Int, b: Int) -> Int {
a + b
}
返回值类型与函数重载无关
默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(C++中会报错)
// 函数名相同,参数个数相同,参数标签相同,参数类型相同
func add(v1: Int, v2: Int) -> Int {
return v1 + v2
}
func add(v1: Int, v2: Int, v3: Int = 10) -> Int {
return v1 + v2 + v3
}
// 会调用add(v1: Int, v2: Int)
add(v1: 10, v2: 20)
可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器有可能会报错
函数类型
每一个函数都是有类型的,函数类型有形式参数类型
、返回值类型
组成
func sum(a: Int, b: Int) -> Int {
return a + b
}
// 定义变量
var fn: (Int, Int) -> Int = sum
print(fn(2,3)) // 输出:5
函数类型可以作为函数参数
func sum(a: Int, b: Int) -> Int {
return a + b
}
func difference(v1: Int, v2: Int) -> Int {
return 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 {
return input + 1
}
func previous(_ input: Int) -> Int {
return input - 1
}
// forward 函数的第一个->是返回值,后面的参数是一个函数代表接收一Int类型的参数返回Int类型
func forward(_ forward: Bool) -> (Int) -> Int {
// 如果参数为true,就调用next方法,否则调用previous方法
return forward ? next : previous
}
print(forward(true)(3)) // 4
print(forward(false)(3)) // 2
typealias
typealias 用来给类型起别名
typealias Byte = Int8
typealias Short = Int16
typealias Long = Int64
// 给元组起别名
typealias Date = (year: Int, month: Int, day: Int)
func test(_ date: Date) {
print(date.0)
print(date.year)
}
test((2011, 9, 11)) // 输出2011 2011
// 给方法起别名
typealias IntFn = (Int, Int) -> Int
func difference(v1: Int, v2: Int) -> Int {
return v1 - v2
}
let fn: IntFn = difference
print(fn(20,10)) // 输出:10
嵌套函数
将函数定义在函数内部的称为嵌套函数
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
}
print(forward(true)(3)) // 4
print(forward(false)(3)) // 2
内联函数
如果开启了编译器优化(Release模式默认情况下会开启优化),编译器会自动将某些函数转为内联函数
调用
- 内联函数其实就是将函数展开成函数体
- 内联函数解决函数调用的效率问题
- 函数之间调用,是内存地址之间的调用,当函数调用完毕之后还会返回原来函数执行的地址。函数调用有时间开销,内联函数就是为了解决这一问题。
内联函数可以解决程序中函数调用的效率问题,通过编译器的预处理,在调用内联函数的地方将内联函数内的语句copy到调用函数的地方,减少一些不必要的开销,但会导致主函数指令增多、函数体积增大等情况。
// 永远不会被内联,即使编译器开启了优化
@inline(never) func test() {
print("test")
}
// 开启编译器优化后,即使代码很长,也会被内联(递归调用函数、动态派发函数除外)
@inline(__always) func test2() {
print("test2")
}
以下几种函数不会自动被内联:
1.函数体比较长
2.包含递归调用
3.包含动态派发等等
内联函数只是我们向编译器提供的申请,编译器不一定采取inline形式调用函数.
内联函数不能承载大量的代码.如果内联函数的函数体过大,编译器会自动放弃内联.
内联函数内不允许使用循环语句或开关语句.
函数的注释
快捷键:option+command+shift
本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.