Swift第一章 基础语法、流程控制、函数
当前语法为swift5.1版本
简介
- Swift是Apple在2014年6月WWDC发布的全新编程语言,中文名和LOGO是“雨燕”
-
编译流程如下:

- OC语言的通过编译器前端Clang编译,然后交给编译器后端LLVM编译,生成不同平台的二进制码。
- Swift语言通过编译器前端swiftc编译,然后交给编译器后端LLVM编译,生成不同平台的二进制码。
swiftc
- swiftc存放在Xcode内部:Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
- 一些操作
- 生成语法树AST: swiftc -dump-ast main.swift
- 生成最简洁的SIL代码:swiftc -emit-sil main.swift
- 生成LLVM IR代码: swiftc -emit-ir main.swift -o main.ll
- 生成汇编代码: swiftc -emit-assembly main.swift -o main.s
Playground
- 不用编写main函数,Swift将全局范围内的首句可执行代码作为程序入口
- 一句代码尾部可以省略分号(;),多句代码写到同一行时必须用分号(;)隔开
- 用var定义变量,let定义常量,编译器能自动推断出变量\常量的类型
- Playground可以快速预览代码效果,是学习语法的好帮手
- 打开Xcode -》file-》new-》Playground…-》iOS下的Blank,选择文件名、保存位置即可
Command + Shift + Enter:运行整个PlaygroundShift + Enter:运行截止到某一行代码
-
Playground - View: 如何创建一个View并展示
import UIKit import PlaygroundSupport let xview = UIView() xview.frame = CGRect(x: 0, y: 0, width: 50, height: 50) xview.backgroundColor = UIColor.red //拿到显示屏PlaygroundPage,然后将当前view展示为xview PlaygroundPage.current.liveView = xview //展示图片 //先将图片logo.png拖拽到左侧的Resources文件夹 let imageView = UIImageView(image: UIImage(named: "logo")) PlaygroundPage.current.liveView = imageView //展示控制器 let vc = UITableViewController() vc.view.backgroundColor = UIColor.blue PlaygroundPage.current.liveView = vc - Playground – 多Page
- 一个Playground下面可以创建多个page,每次训练的代码放到一个page即可
- 右击当前的Playground-》New Playground page,然后取名即可
- 然后再当前page下练习Swift代码
- Markup语法
- Playground的注释支持markup语法(与markdown相似)
- 开启markup渲染效果:Editor -> Show Rendered Markup
- 注意:Markup只在Playground中有效
//: 开始markup,单行开启 /*: 开始markup,多行开启 */
基础语法
常量
- 只能赋值1次,它的值不要求在编译时期确定,但使用之前必须赋值1次
- 常量、变量在初始化之前,都不能使用
//编译时赋值
let age1 = 10
//编译时age2、age3都没有赋值,真正运行时才赋值
let age2: Int
age2 = 20
func getAge() -> Int {
return 30
}
let age3 = getAge()
//常量、变量在初始化之前,都不能使用
let age: Int
print(age)
//下面代码错误,声明变量agex时,无法确认类型。
let agex
agex = 20
标识符
- 标识符(比如常量名、变量名、函数名)几乎可以使用任何字符
- 标识符不能以数字开头,不能包含空白字符、制表符、箭头等特殊字符
常见数据类型
- 值类型(value type)
- 枚举(enum):Optional
- 结构体(struct):Bool、Int、Float、Double、Character、String、Array、Dictionary、Set,Int本质就是struct类型,可以看Int定义
- 引用类型(reference type):类(class)
- 整数类型: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位
字面量
//布尔
let bool=true //取反是false
// 字符串
let string ="小xxx"
//字符(可存储ASCII字符、Unicode字符)
let character:Character="o"
//整数和浮点数可以添加额外的零或者添加下划线来增强可读性
100_0000、1_000_000.000_000_1、000123.456
//整数
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 // 十六进制,意味着15x2^2,相当于十进制的60.0
let doubleHexadecimal2 = 0xFp-2 // 十六进制,意味着15x2^-2,相当于十进制的3.75
/*以下都是表示12.1875
十进制:12.1875、1.21875e1
十六进制:0xC.3p0 */
//数组
let array =[1,3,5,7,9]
// 字典
let dictionary=["age":18,"height" :168,"weight":120]
类型转换
//整数转换
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
元组(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
- if后面的条件可以省略小括号,条件后面的大括号不可以省略,if后面的条件只能是Bool类型
let age = 4
if age >= 22 {
print("Get married")
} else if age >= 18 {
print("Being a adult")
} else if age >=7 {
print("Go to school")
} else {
print("Just a child")
}
//报错:该条件不是bool,因此报错,即不支持非零即真
if age {
}
while、repeat-while
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次
- repeat-while相当于C语言中的do-while
- 这里不用num–,是因为:从Swift3开始,去除了自增(++)、自减(–)运算符
for
-
闭区间运算符:a…b, a <= 取值 <= b
let names =["Anna","Alex","Brian","Jack" ] for i in 0...3 { print(names[i]) }// Anna Alex Brian Jack let range = 1...3 for i in range { print(names[i]) }// Alex Brian Jack let a=1 var b=2 for i in a...b { print(names [i]) }// Alex Brian for i in a...3 { print(names[i]) }// Alex Brian Jack // i默认是let,有需要时可以声明为var for var i in 1...3 { i += 5 print(i) }//678 //如果for循环中没有使用到i,可以用_代替,防止编译器提示警告i没有使用 for _ in 1...3 { print("for") } -
半开区间运算符:a..<b, a <= 取值 < b
for i in 1..<5 { print(i) }//1234
for – 区间运算符用在数组上
-
举例
let names = ["Anna", "Alex", "Brian", "Jack"] for name in names[0...3] { print(name) } // Anna Alex Brian Jack -
单侧区间:让区间朝一个方向尽可能的远
for name in names[2...] { print(name) } // Brian Jack for name in names[...2] { print(name) } // Anna Alex Brian for name in names[..<2] { print(name) } // Anna Alex -
如果不在数组中使用
let range = ...5 range.contains(7) // false range.contains(4) // true range.contains(-3) // true
区间类型
-
举例:
let range1: ClosedRange<Int> = 1...3 let range2: Range<Int> = 1..<3 let range3: PartialRangeThrough<Int> = ...5 -
字符、字符串也能使用区间运算符,但默认不能用在for-in中
let stringRange1 = "cc"..."ff" // ClosedRange<String> stringRange1.contains("cb") // false stringRange1.contains("de") // true stringRange1.contains("fg") // false let stringRange2 = "a"..."f" stringRange2.contains("d") // true stringRange2.contains("h") // false // \0到~囊括了所有可能要用到的ASCII字符 let characterRange: ClosedRange<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后面不能写大括号{}
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 -
默认可以不写break,并不会贯穿到后面的条件
var number = 1 switch number { case 1: print("number is 1") case 2: print("number is 2") default: print("number is other") } // number is 1 -
使用fallthrough可以实现贯穿效果
var number = 1 switch number { case 1: print("number is 1") fallthrough case 2: print("number is 2") default: print("number is other") } // number is 1 // number is 2 -
switch注意点
- switch必须要保证能处理所有情况
-
case、default后面至少要有一条语句,如果不想做任何事,加个break即可
var number = 1 switch number { case 1: print("number is 1") case 2: print("number is 2") //1. 这里必须加上default代表除了1、2的其他情况 //2. default后面必须有至少一条语句 default: break } -
如果能保证已处理所有情况,也可以不必使用default
enum Answer { case right, wrong } let answer = Answer.right switch answer { //case Answer.right: // 由于已确定answer是Ansewer类型,因此可以省略Answer case .right: print("right") case Answer.wrong: print("wrong") } -
switch也支持Character、String类型
let string = "Jack" switch string { case "Jack": fallthrough case "Rose": print("Right person") default: break } // Right person switch string { case "Jack", "Rose": print("Right person") default: break } // Right person let character: Character = "a" switch character { case "a", "A": print("The letter A") default: print("Not the letter A") } // The letter A -
区间匹配、元组匹配
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 -
值绑定
let point = (2, 0) switch point { //y坐标匹配,因此将2赋值给x 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
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
//where放到for循环中
// 将所有正数加起来
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
标签语句
outer: for i in 1...4 {
for k in 1...4 {
if k == 3 {
//使用标签outer控制外循环
continue outer
}
if i == 3 {
//使用标签outer控制外循环
break outer
}
print("i == \(i), k == \(k)")
}
}
函数
函数的定义
- 函数的形参默认是let,也只能是let,因此let可以直接省略
-
举例:
//1. 无参有返回值 func pi() -> Double { return 3.14 } pi() //2. 有参、有返回值 func sum(v1: Int, v2: Int) -> Int { return v1 + v2 } sum(v1: 10, v2: 20) //3. 无参、无返回值 func sayHello() -> Void { print("Hello") } func sayHello() -> () { print("Hello") } func sayHello() { print("Hello") }
隐式返回(Implicit Return)
-
如果整个函数体是一个单一表达式,那么函数会隐式返回这个表达式,不用写return
func sum(v1: Int, v2: Int) -> Int { //函数体就是一个单一表达式 v1 + v2 } sum(v1: 10, v2: 20) // 30
返回元组:实现多返回值
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
函数的文档注释
- 参考:https://swift.org/documentation/api-design-guidelines/
/// 求和【概述】
///
/// 将2个整数相加【更详细的描述】
///
/// - Parameter v1: 第1个整数
/// - Parameter v2: 第2个整数
/// - Returns: 2个整数的和
///
/// - Note:传入2个整数即可【批注】
///
func sum(v1: Int, v2: Int) -> Int {
v1 + v2
}
参数标签(Argument Label)
-
可以修改参数标签
//修改time这个参数标签为at func goToWork(at time: String) { //函数体里面还用修改之前的标签 print("this time is \(time)") } //调用时,函数标签用at,好处是:看起来表达通顺 goToWork(at: "08:00") // this time is 08:00 -
可以使用下划线
_省略参数标签func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 } sum(10, 20)
默认参数值(Default Parameter Value)
-
参数可以有默认值
func check(name: String = "nobody", age: Int, job: String = "none") { print("name=\(name), age=\(age), job=\(job)") } check(name: "Jack", age: 20, job: "Doctor") // name=Jack, age=20, job=Doctor check(name: "Rose", age: 18) // name=Rose, age=18, job=none check(age: 10, job: "Batman") // name=nobody, age=10, job=Batman check(age: 15) // name=nobody, age=15, job=none - C++的默认参数值有个限制:必须从右往左设置。由于Swift拥有参数标签(比如name),因此并没有此类限制
-
但是在省略参数标签时,需要特别注意,避免出错
// 这里的middle不可以省略参数标签 func test(_ first: Int = 10, middle: Int, _ last: Int = 30) { } test(middle: 20)
可变参数(Variadic Parameter)
-
一个函数最多只能有1个可变参数
func sum(_ numbers: Int...) -> Int { var total = 0 for number in numbers { total += number } return total } sum(10, 20, 30, 40) // 100 -
紧跟在可变参数后面的参数不能省略参数标签
// 参数string不能省略标签,string标签不能被省略 func test(_ numbers: Int..., string: String, _ other: String) { } test(10, 20, 30, string: "Jack", "Rose")
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") -
使用
print(1, 2, 3, 4, 5) // 1 2 3 4 5 print(1, 2, 3, 4, 5, separator: "_") // 1_2_3_4_5 print("My name is Jake.", terminator: "") print("My age is 18.") // My name is Jake.My age is 18.
输入输出参数(In-Out Parameter)
- 前面说过,函数的参数是let类型,那么如果要修改函数的参数呢?–inout
- 可以用inout定义一个输入输出参数:可以在函数内部修改外部实参的值
- 可变参数不能标记为inout
- inout参数不能有默认值
- inout参数只能传入可以被多次赋值的,即num1、num2不能用let修饰
-
inout参数的本质是地址传递(引用传递)
func swapValues(_ v1: inout Int, _ v2: inout Int) { //通常情况下,v1、v2都默认是let,不能被修改的 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) }
函数重载(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: Int, v2: Double) -> Double { Double(v1) + v2 } // 参数类型不同 func sum(v1: Double, v2: Int) -> Double { v1 + Double(v2) } // 参数标签不同 func sum(_ v1: Int, _ v2: Int) -> Int { v1 + 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, v2: 20.0) // 30.0 sum(v1: 10.0, v2: 20) // 30.0 sum(10, 20) // 30 sum(a: 10, b: 20) // 30
函数重载注意点
-
返回值类型与函数重载无关
func sum(v1: Int, v2: Int) -> Int { v1+ v2} func sum(v1: Int, v2: Int) {} //报错,上面两个函数不是重载 sum(v1:10,v2:20) -
默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(在C++中会报错)
func sum(v1: Int, v2: Int) -> Int { v1 + v2 } func sum(v1: Int, v2: Int, v3: Int = 10) -> Int { v1 + v2 + v3 } // 会调用sum(v1: Int, v2: Int) sum(v1: 10, v2: 20) -
可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器有可能会报错
func sum(v1: Int, v2: Int) -> Int { v1 + v2 } func sum(_ v1: Int, _ v2: Int) -> Int { v1 + v2 } func sum(_ numbers: Int...) -> Int { var total = 0 for number in numbers { total += number } return total } //ambiguous这个无误要牢记,代表二义性,歧义 // error: ambiguous use of 'sum' sum(10, 20)
内联函数(Inline Function)
-
如果开启了编译器优化(Release模式默认会开启优化),编译器会自动将某些函数变成内联函数,将函数调用展开成函数体

- 哪些函数不会被自动内联?
- 函数体比较长
- 包含递归调用
- 包含动态派发
-
@inline// 永远不会被内联(即使开启了编译器优化) @inline(never) func test() { print("test") } // 开启编译器优化后,即使代码很长,也会被内联(递归调用函数、动态派发的函数除外) @inline(__always) func test() { print("test") } - 在Release模式下,编译器已经开启优化,会自动决定哪些函数需要内联,因此没必要使用
函数类型(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 } //forward返回值为(Int) -> Int类型 func forward(_ forward: Bool) -> (Int) -> Int { forward ? next : previous } forward(true)(3) // 4 forward(false)(3) // 2 -
返回值是函数类型的函数,叫做高阶函数(Higher-Order Function)
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, 10)) typealias IntFn = (Int, Int) -> Int 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 = ()
嵌套函数(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