如果学习的硬件和软件资源已经具备,那么接下来的事情就是保持一颗永远向上的心,如果学习累了,遇到困难了,遇到挫折了,那么就想想那些精美的、绝妙的app,想想这些app将来也会出自你的手中,相信每一位同学都可以成为IOS开发大师的。 ————慕课网讲师·刘宇波

学习swift最简单的方法是使用Xcode的playground。

基本类型

常量和变量

常量let

swift特意将常量的优先级提高到和变量相同实际上是在鼓励我们使用常量,思考程序什么时候使用常量。这样程序可以进行更好的优化。

变量var

1
2
var a = 2,b = 3,c = "hello swift",d = 3.2
// a = "abc" cannot assign a value of type 'String' to a value of type 'Int'

以上的代码中我们并没有显式指定变量的类型,那么是否swift就和js一样是弱类型的语言呢?答案是否定的。swift是强类型的语言。我们可以按住option键然后点击变量名称查看变量类型。我们还可以显式声明变量的类型,如下:

1
2
let a, b, c:Double
var imInt:Int = 80

swift语言有一种Type inference机制能够自动确定变量的类型。
整型的存储是有范围的,具机器芯片架构而定。我们可以使用Int.maxInt.min来查看,当整型溢出的时候在编译时刻即可察觉。所以它宣称自己是一门安全的语言。除了整型外,还有UIntInt8,Int16等等。

1
2
var a:Int = 10
a = 9999999999999999999 // integer literal '9999999999999999999' overflows when stored into 'Int'

swift提供了一个很酷的特定,可以使用下划线分隔数字,从而更方便表示位数。

1
2
3
var a:UInt = 10
a = 100_0000 // 1000000
a = 100_000_000 // 10000000

和很多的编程语言不一样,swift并不提供隐式类型转换(因为很多的错误根源在此处)。

1
2
3
4
5
6
7
let x:UInt16 = 100
let y:UInt8 = 10
x + y // overloads for '+' exist with these partially matching parameter lists: (UInt8, UInt8), (UInt16, UInt16)

// 以下是正确的代码
let m = x + UInt16(y)
let n = UInt8(x) + y

元组

将多个不同的值集合到一个数据中。可以有任意多个值,不同的值可以是不同的类型。适合轻量级的数据聚合。

1
2
3
// Tuple
var point = (5,2) // 使用元组表示二维平面中的点
var httpResponse = (404,"Not Found") // 使用元组保存http状态码

我们也可以显示指定元组中各个字段类型,可以使用.序号的方式拿到它的分量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let httpResponse:(Int,String) = (200,"OK")
httpResponse.0
httpResponse.1

let point = (x:1,y:2)
point.x
point.y

// 命名元组分量
let p:(x:Int,y:Int) = (7,8)
p.x
p.y

let loginRes = (true ,"consoles")
let ( isLoginSuccess ,_ ) = loginRes // 我们仅关心第一个元素解包的值
if isLoginSuccess {
print("login success")
} else {
print("login failure")
}

元组解包

1
2
3
4
let httpResponse:(Int,String) = (200,"OK")
let (code,msg) = httpResponse
code // 200
msg // "OK"

在Swift中多行注释非常智能,我们可以在多行注释中插入任何内容(包括多行注释)不会带来解析问题。

运算符

区间运算符

  • 闭区间运算符:a...b
  • 前闭后开区间运算符:a..<b

for in循环要求我们必须将范围里面的值跑一遍不可跳过。

1
2
3
4
5
6
7
for index in 1...10{
index // 1,2,3,4 ... 10
// index = 5 此处的index是let类型,尝试修改会报错
}
for index in 1..<10{
index
}

有时候我们不需要知道循环中每个元素的具体值,仅仅知道它们执行的次数就行了,这时我们可以就像元组解包那样使用_来忽略值,例如以下的程序计算2^10:

1
2
3
4
5
6
var result = 1
let base = 2
let pow = 10
for _ in 1...pow{
result *= base
}

控制流

  • 和其他语言类似,只不过swift中的do-while循环被改变成了repeat-while循环。
  • swift中case语句默认有break,防止case穿透。对于其他语言中的多个case执行同一个语句的时候只需要在case中使用都逗号分隔。

swift的高级用法

swift的注意事项

我们要枚举出switch中所有的情况,如果我们的case无法匹配所有的情况,就需要添加default语句。

1
2
3
4
5
6
7
8
9
10
11
let a = 1

switch a {
case -1:
print(1)
case 0:
print(0)
case 1:
print(1)
//default:() 这里不能注释,因为我们要枚举所有的Int值或者我们也可以使用default:break
}

switch语句扩充了对元组的支持

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let vector = (1,1)
switch vector{
case (0,0):
print("原点")
case (1,0):
print("x轴正方向上的单位向量")
case (-1,0):
print("x轴负方向上的单位向量")
case (0,1):
print("y轴正方向上的单位向量")
case (0,-1):
print("y轴负方向上的单位向量")
default:
print("仅仅是一个普通向量")
}

我们也可以使用下划线来忽略元组中某一维度的值,从而一定程度上做到了模式匹配。我们也可以对元组中的某个分量进行区间运算例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
let vector = (x:1,y:1)
switch vector{
case (0,0):
print("原点")
case (_,0):
print("(\(vector.x),\(vector.y))向量在x轴上")
case (0,_):
print("(\(vector.x),\(vector.y))向量在y轴上")
case (-2...2,-2...2):
print("(\(vector.x),\(vector.y))向量在原点附近")
default:
print("仅仅是一个普通向量")
}

元组的解包也可以融入到case语句中:

1
2
3
4
5
6
7
8
9
10
11
12
let point:(Int,Int) = (8,8)
switch point{
case (0,0):
print("原点")
case (let x,0):
print("在x轴上,x = \(x)")
case (0,let y):
print("在y轴上,y = \(y)")
case (let x,let y):
print("普通向量:(\(x),\(y))")
}
// 以上的语句不需要default,因为已经涵盖了所有的可能

对于常用的case贯穿的情况,swift提供了fallthrough关键字,能够在执行完一个case之后跳转到下一个case。

1
2
3
4
5
6
7
8
9
10
11
12
13
let point:(Int,Int) = (0,0)
switch point{
case (0,0):
print("原点")
fallthrough
case (_,0):
print("在x轴上")
fallthrough
case (0,_):
print("在y轴上")
default:
print("普通向量")
}

看一个实际问题:找出方程x^4 - y^2 = 15 * x * y在300以内的第一个整数解,使用swift,我们可以这样:

1
2
3
4
5
6
7
8
9
10
// 给最外层的循环设置一个label
findAnswer:
for m in 1...300{
for n in 1...300{
if m * m * m * m - n * n == 15 * m * n{
print("找到一组解,m = \(m),y = \(n)")
break findAnswer
}
}
}

以上就给swift加上了goto语句的特性。

switch中的where

1
2
3
4
5
6
7
8
9
let p = (3,3)
switch p {
case let (x,y) where x == y: // 先解包然后执行判断逻辑
print("点在y=x这条正比例函数上")
case (let x,let y) where x == -y:
print("点在y=-x这条正比例函数上")
case let (x,y):
print("普通点")
}

if case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
let age = 19
switch age {
case 10...19:
print("青少年")
default:
print("成年")
}

if case 10...19 = age{
print("青少年")
}

if case 10...19 = age where age > 18{
print("青少年,大学生")
}

for i in 1...100{
if i % 3 == 0{
print(i)
}
}

for case let i in 1...100 where i % 3 == 0 {
print(i)
}

guard关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func buy(money:Int,price:Int,capacity:Int,volume:Int){
if money > price {
if capacity > volume {
print("买买买,剩余的钱:\(money - price),背包容量:\(capacity - volume)")
} else {
print("背包容量不够")
}
} else {
print("钱不够")
}
}

func buy2(money:Int,price:Int,capacity:Int,volume:Int){
guard money >= price else {
print("Not engugh money")
return
}
guard capacity >= volume else {
print("Not engugh capacity")
return
}
// 函数主体
print("买买买,剩余的钱:\(money - price),背包容量:\(capacity - volume)")
}

以上的逻辑很容易使用if-else实现,但是苹果为什么会推崇使用guard关键字呢,它就是提倡将于业务无关边界判断剥离开来,踏踏实实集中于程序的功能。

字符串