Swift基本语法
Swift简介
- Swift是Apple在2014年6月WWDC发布的全新编程语言,中文名和logo是“雨燕”
- Swift之父Chris Lattner
- Clang编译器作者、LLVM项目的主要发起人
- 从Apple离职后,先后跳槽到Tesla、Google
- 目前在Google Brain从事AI研究
Swift版本
- Swift5.x版本,ABI稳定
- API(Application Programming Interface): 应用程序编程接口
- 源代码和库之间的接口
- ABI (Application Binary Interface):应用程序二进制接口
- 应用程序与操作系统之间的底层接口
- 涉及的内容有:目标文件格式、数据类型的大小、布局、对齐、函数调用约定等等
- API(Application Programming Interface): 应用程序编程接口
- 随着ABI的稳定,Swift语法基本不会再有太大的改动
- Swift完全开源: htps://github.com/apple/swift,主要采用C++编写
Swift编译流程
Swift Code --> Swift AST --> Raw Swift IL --> Canonical Swift IL --> LLVM IR --> Assembly --> Executable
swiftc
- swiftc存放在Xcode内部
- Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
- 一些操作
- 生成语法树: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 - View
import UIKit
import PlaygroundSupport
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
view.backgroundColor = UIColor.blue
PlaygroundPage.current.liveView = view
Playground - 注释
- 单行注释 //
- 多行注释 /* */
- 多行嵌套注释 /* 1 /* 3 */ 2 */
- 支持markup语法 添加冒号 //: 等
常量
- 只能赋值一次
- 它的值不要求在编译时期确定,但使用之前必须赋值一次
- 变量、常量在初始化之前,都不能使用
标识符
- 标识符(比如常量名、变量名、函数名)几乎可以使用任何字符
- 标识符不能以数字开头,不能包含空白字符、制表符、箭头灯特殊字符
常见数据类型
- 值类型 (value type)
- 枚举(enum): Optional
- 结构体(struct): Bool、Int、Float、Double、Character、String、Array、Dictionary、Set
- 引用类型 (reference type)
- 类(class)
字面量
- 布尔类型
let bool = true - 字符串
let string = "Hello word!" - 字符(可储存ASCII字符、Unicode字符)
let character: Character = "A" - 浮点数
let double = 12.0 - 数组
let array = [1, 3, 3, 4] - 字典
let dictionary = ["age": 19, "height": 123]
元祖(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.description)")
流程控制
if-else
let age = 5
if age >= 22 {
print("Get married")
} else {
print("Just a child")
}
- if 后面的条件可以省略小括号,条件后面的大括号不能省略
- if 后面的条件只能是Bool类型
while
- repeat-while相当于C语言中的do-while
for
- 闭区间运算符: a...b, a <= 取值 <= b
- 半开区间运算符: a...<b, a <= 取值 < b
let names = ["Anna", "Lele", "Brian", "Jack"]
for i in 0...3 {
print(names[i])
}
let range = 1...3
for i in range {
print(names[i])
}
for var i in 1...3 {
i += 5
print(i)
}
for _ in 1...3 {
print("for")
}
for i in 0...<5 {
print(names[i])
}
- 区间运算符用在数组上
- 单侧区间:让区间朝一个方向尽可能的远
let names = ["Anna", "Lele", "Brian", "Jack"]
for i in names[0...3] {
print(i)
}
for i in names[...2] {
print(i)
}
- 带间隔的区间值
let hours = 11
let hourInterval = 2
for tickMark in stride(from: 4, through: hours, by: hourInterval) {
print(tickMark)
}
switch
- case、default后面不能写大括号{}
- 默认可以不写break,并不会贯穿到后面的条件
- 使用 fallthrough 可以实现贯穿效果
- switch注意点
- switch必须保证能处理所有情况
- case、default后面至少要有一条语句
- 如果不想做任何事,加个break即可
- 如果能保证已处理所有的情况,可以不必使用default
- case匹配属于模式匹配
- switch也支持Character、String类型
- switch 复合条件 case "Jack","Rose"
- 区间匹配 case 0...3
- 元祖匹配 case (0, 0)
- 值绑定 case (let x, 0)只要求一方符合
- where条件 case let (x, y) where x == y
标签语句
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)")
}
}
函数
函数的定义
- func pi() -> Double
- 形参默认是let,也只能 是let
- 无返回值 func pi()
- 隐式返回 如果整个函数体是一个单一的表达式,那么函数会隐式返回这个表达式 省略return
返回元祖: 实现多返回值
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
参数标签
- 可以修改参数标签 func goToWork(at time: String){} at 用在函数里,是参数标签, time用在函数外
- 可以使用 _ 省略参数标签 func goToWork(_ time: String){}
默认参数 (Default Parameter Value)
- 参数可以有默认值 func check(name: String = "body") {}
- C++ 的默认参数数值有个限制: 必须从右向左设置。由于Swift拥有参数标签,因此并没有此类限制
- 在省略参数标签时,需要特别注意
可变参数(Variadic Parameter)
func sum(_ numbers: Int...) -> Int {
var total = 0
for number in numbers {
total += number
}
return total
}
sum(10, 20, 30, 40) // 100
- 一个函数最多只能有1个可变参数
- 紧跟在可变参数后面的参数不能省略参数标签
print函数
- public func print(_ items: Any..., separator: String = " ", terminator: String = "\n")
输入输出函数(In-Out Parameter)
- 可以用inout定义一个输入输出函数: 可以在函数内部修改外部实参的值
- 比如交换两个参数值
func swapValues(_ v1: inout Int, _ v2: inout Int) { (v1, v2) = (v2, v1) }
- 可变参数不能标记inout
- inout参数不能有默认值
- inout参数本质是地址传递(引用传递)
- inout参数只能传入可以被多次赋值的
函数重载(Function Overload)
-
规则
- 函数名相同
- 参数个数不同 || 参数类型不同 || 参数标签不同
-
注意点
- 返回值类型与函数重载无关
- 默认参数值和函数重载一起使用产生二义性时,编译器并不会报错(c++ 报错)
- 可变参数、省略参数标签、函数重载一起使用产生二义性时,编译器有可能会报错
内联函数(Inline Function)
- 如果开启了编译器优化(Release模式会默认开启优化 搜索 optimization), 编译器会自动将某些函数变成内联函数
- 内联函数将函数调用展开成函数体
- 哪些函数不会内联
- 函数体特别长
- 包含递归调用
- 包含动态派发(即oc里的动态绑定)
![]()
函数类型(Function Type)
- 每一个函数都是有类型的,函数类型由形参类型、返回值类型组成
- 函数类型作为函数参数
- 函数类型作为返回值(返回值是函数类型的函数,叫做高阶函数: Higher-Order Function)
- typealias 用来给类型起别名 (按照Swift标准库定义, Voide就是空元祖 typealias Void = ())
嵌套函数
- 将函数定义在函数内部
枚举
基本用法
enum Direction {
case north, south, east, west
}
关联值(Associated Values)
- 将枚举的成员值跟其他类型的关联储存在一起,会非常有用
enum Date {
case digit(year: Int, month: Int, day: Int)
case string(String)
}
var date = Date.digit(year: 2011, month: 9, day: 11)
date = .string("2019-10-9")
switch date {
case .digit(let year, let month, let day):
print(year,month,day)
case let .string(value):
print(value)
}
// let 也可以改为 var
原始值(Raw Value)
- 枚举成员可以使用 相同累心 的默认值预先关联,这个默认值就叫做: 原始值
enum PokerSuit: Character { case spade = "A" }
隐式原始值(Implicitly Assigned Raw Values)
- 如果枚举的原始值类型是Int、String,Swift会自动分配原始值
- 如果是字符串,就是名字
- 如果是Int,就是0、1、2
enum Direction : String {
case north = "north"
case south = "south"
case east = "east"
case west = "west"
}
enum Direction : String {
case north, south, east, west
}
// 这两个枚举写法完全等价
递归枚举(Recursive Enumeration)
- 在enum前必须加上indirect关键字
indirect enum AS {}
MemoryLayout
- 可以使用MemoryLayout获取数据类型占用的内存大小
// 关联值 - 直接将传入的关联值存储在枚举变量的内存里
enum Password {
case number(Int, Int, Int, Int)
case other
}
var pwd = Password.number(5, 6, 4, 7)
pwd = .other
MemoryLayout<Password>.size //33,实际用到的控件大小--32个字节存储number,一个存储other
MemoryLayout<Password>.stride //40,分配占用的空间大小
MemoryLayout<Password>.alignment//8,对齐参数
// 原始值 - 和枚举成员绑定的,固定的,并不会将原始值存储到枚举变量内存里
enum Direction : Int {
case north = 1, south, east, west
}
var d = Direction.north
MemoryLayout<Direction>.size //1
MemoryLayout<Direction>.stride //1
MemoryLayout<Direction>.alignment//1
可选项(Optional)
- 可选项,一般也称为可选类型,它允许将值设置为nil
- 在类型名称后加一个?来定义一个可选项
var age: Int? //表示的是初始值默认是nil
强制解包(Forced Unwrapping)
- 可选项是对其他类型的一层包装,可以将它理解为一个盒子
- 如果为nil,那么他就是个空盒子
- 如果不是nil,那么盒子里转的是被包装类型的数据
- 如果要从可选项中取出被包装的数据(将盒子里的东西取出来),需要使用感叹号!进行强制解包
- 如果对值为nil的可选项(空盒子)进行强制解包,将会产生运行时错误
可选项绑定(Optional Binding)
- 可以使用可选项绑定来判断可选项是否包含值
- 如果包含就会自动解包,把值赋给一个临时的常量或者变量,并返回true,否则返回false
if let number = Int("123") {
print("\(number)")
} else {
print("shibai")
}
- 可选项绑定,多个条件要用","区别开来
if let number = Int("123"), let s = Int("11") {
print("\(number)")
} else {
print("shibai")
}
- while循环中使用可选项绑定
// 遍历数组,将遇到的正数加起来,如果遇到负数或者非数字,停止遍历
var strs = ["10", "20", "abc", "-20", "30"]
var index = 0
var sum = 0
while let num = Int(strs[index]), num > 0 {
sum += num
index += 1
}
print(sum)
空合并运算符(Nil-Coalescing Operator)
- a ?? b
- a 是可选项
- b 是可选项,也可以不是
- b 和 a 的存储类型必须相同
- 如果 a 不为nil,就返回a
- 如果a 为nil,就返回b
- 如果b 不是可选项, 返回a 时会自动解包
let a: Int? = 7
let b: Int = 2
let c = a ?? b // c 是 Int, 1
- a ?? b ?? c 从左往右计算
- ?? 和 if let 配合使用
if let c = a ?? b {}
guard语句
- guard 条件 else
- 当guard语句的条件为false时,就会执行大括号里的代码
- 当guard语句的条件为true时,就会跳过guard语句
- guard语句特别适合做提前退出
- 当使用guard语句进行可选项绑定时,绑定的常量、变量也能在外层作用域中使用
// 使用 guard else 实现用户登录
func login (_ info: [String : String]) {
guard let username = info["usernmae"] else {
print("请输入用户名")
return
}
guard let password = info["password"] else {
print("请输入密码")
return
}
print("用户名: \(username)", "密码: \(password)", "登录中...")
}
隐式解包(Implicitly Unwrapped Optional)
- 在某些情况下,可选项一旦被设定值之后,就会一直拥有值,在这种情况下,可以去掉检查,也不必每次访问的时候都进行解包,因为它能确定每次访问的时候都有值
- 可以在类型后面加个感叹号!,定义一个隐式解包的可选项
// 隐式解包的可选项
let num1: Int! = 10
let num2: Int = num1
字符串插值
-
可选项在字符串插值或者直接打印时,编译器会发出警告
var age: Int? = 10; print("My age is \(age)") -
三种消除警告的方法
print("My age is \(age!)")print("My age is \(String(describing: age))")print("My age is \(age ?? 0)")
多重可选项
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
print(num2 == num3) // true
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
print(num2 == num3) // false
print(num1 == num3) // false
(num2 ?? 1) ?? 2 // 2
(num3 ?? 1) ?? 2 // 1
- 可以使用lldb指令 frame variable -R 或者 fr v -R 查看区别
多重可选项
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 10
print(num2 == num3) // true
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
print(num2 == num3) // false
print(num1 == num3) // false
(num2 ?? 1) ?? 2 // 2
(num3 ?? 1) ?? 2 // 1
- 可以使用lldb指令 frame variable -R 或者 fr v -R 查看区别
var num1: Int? = 10
var num2: Int?? = num1
var num3: Int?? = 20
/*
(lldb) fr v -R num1
(Swift.Optional<Swift.Int>) num1 = some {
some = {
_value = 10
}
}
(lldb) fr v -R num2
(Swift.Optional<Swift.Optional<Swift.Int>>) num2 = some {
some = some {
some = {
_value = 10
}
}
}
(lldb) fr v -R num3
(Swift.Optional<Swift.Optional<Swift.Int>>) num3 = some {
some = some {
some = {
_value = 20
}
}
}
*/
var num1: Int? = nil
var num2: Int?? = num1
var num3: Int?? = nil
/*
(lldb) fr v -R num1
(Swift.Optional<Swift.Int>) num1 = none {
some = {
_value = 0
}
}
(lldb) fr v -R num2
(Swift.Optional<Swift.Optional<Swift.Int>>) num2 = some {
some = none {
some = {
_value = 0
}
}
}
(lldb) fr v -R num3
(Swift.Optional<Swift.Optional<Swift.Int>>) num3 = none {
some = some {
some = {
_value = 0
}
}
}
*/
可选项的本质
- 本质是 enum 类型
enum Optional<Wrapped>: ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ value: Wrapped) {
}
}
var age: Int? = 10
// 等价于
var age: Optional<Int> = .some(10)
var age: Int? = 20
switch age {
case let v:
print(v)
default:
break
}
// 如果 age = nil 那么,这个 case let v? 就不会执行
switch age {
case let v?: // 会解包
print(v)
default:
break
}
// 其实就相当于
switch age {
case let .some(v):
print(v)
default:
break
}
--------------------------执行结果--------------------------
Optional(20)
20


浙公网安备 33010602011771号