Swift基础语法
一.变量和常量
1.定义
-
let
定义常量,一经赋值不允许再修改 -
var
定义变量,赋值之后仍然可以修改
//: # 常量
//: 定义常量并且直接设置数值
let x = 20
//: 常量数值一经设置,不能修改,以下代码会报错
// x = 30
//: 使用 `: 类型`,仅仅只定义类型,而没有设置数值
let x1: Int
//: 常量有一次设置数值的机会,以下代码没有问题,因为 x1 还没有被设置数值
x1 = 30
//: 一旦设置了数值之后,则不能再次修改,以下代码会报错,因为 x1 已经被设置了数值
// x1 = 50
//: # 变量
//: 变量设置数值之后,可以继续修改数值
var y = 200
y = 300
2.自动推导
- Swift能够根据右边的代码,推导出变量的准确类型
- 通常在开发时,不需要指定变量的类型
- 如果要指定变量,可以在变量名后使用:,然后跟上变量的类型
重要技巧:Option + Click 可以查看变量的类型
3.没有隐式转换!!!
- Swift 对数据类型要求异常严格
- 任何时候,都不会做隐式转换
如果要对不同类型的数据进行计算,必须要显式的转换
let x2 = 100
let y2 = 10.5
let num1 = Double(x2) + y2
let num2 = x2 + Int(y2)
4.let & var 的选择
- 应该尽量先选择常量,只有在必须修改时,才需要修改为
var
- 在 Xcode 中,如果没有修改变量,Xcode 会提示修改为
let
二.Optional 可选类型
1.介绍
-
Optional
是 Swift 的一大特色,也是 Swift 初学者最容易困惑的问题 - 定义变量时,如果指定是
可选的
,表示该变量可以有一个指定类型的值,也可以是 nil
- 定义变量时,在类型后面添加一个
?
,表示该变量是可选的 - 变量可选项的默认值是
nil
- 常量可选项没有默认值,主要用于在构造函数中给常量设置初始数值
// Optional 可能有两个值 (None / Some)
// 格式(1. 自动推导):var 变量名: Optional = 值
let x: Optional = 20
// 格式(2. 使用泛型):var 变量名:Optional<类型> = 值
let y: Optional = 30
// 格式(3. 简化格式):var 变量名: 类型? = 值
let z: Int? = 10
// 可选值在参与计算前,必须`解包 unwarping`
// 只有`解包(unwrap)`后才能参与计算
// 在变量后添加一个 `!`,可以强行解包
// `?` 是用于定义的
// `!` 是用于解包的,程序员要对每一个 `!` 负责
print(x! + y! + z!)
- 如果 Optional 值是
nil
,不允许参与计算 - 只有
解包(unwrap)
后才能参与计算 - 在变量后添加一个
!
,可以强行解包
注意:必须要确保解包后的值不是 nil,否则会报错
// 常量可选项必须要设置初始值
let x: Int? //= 20
x = nil
// 变量可选项默认值是 nil
var y: Int? //= 10
y = 20
// unexpectedly found nil while unwrapping an Optional value
// 在对可选项解包时发现 nil,会报错!
if y == nil || x == nil {
print("y 或 x 为 nil")
} else {
print(x! + y!)
}
常见错误
unexpectedly found nil while unwrapping an Optional value
翻译
在对可选项[解包]时发现 nil
2.??
运算符 (空合并运算符)
-
??
运算符可以用于判断变量/常量
的数值是否是nil
,如果是则使用后面的值替代 - 在使用 Swift 开发时,
??
能够简化代码的编写
var num: Int?
let r1 = (num ?? 0) + 10
print(r1)
三.控制流
1.if
- Swift 中没有 C 语言中的
非零即真
概念 - 在逻辑判断时必须显示地指明具体的判断条件
true
/false
- if 语句条件的
()
可以省略 - 但是
{}
不能省略
func demo1(num: Int) -> () {
if num > 10 {
print("大 \(num)")
} else {
print("小 \(num)")
}
}
2.三目运算
- Swift 中的
三目
运算保持了和 OC 一致的风格
func demo2() -> () {
var a = 10
var b = 20
let c = a > b ? a : b
print(c)
}
适当地运用三目,能够让代码写得更加简洁
3.可选项判断
- 由于可选项的内容可能为
nil
,而一旦为nil
则不允许参与计算 - 因此在实际开发中,经常需要判断可选项的内容是否为
nil
- 如果单纯使用 if,会让代码嵌套层次很深,让代码不宜阅读和维护,为了解决这一问题,苹果提供了以下三种方式
- ??
- if
let / var
- guard
let / var
else
4.if let / var
func demo3(name: String?, age: Int?) {
// 方法1: 强行解包 - 一旦出现 nil 值,程序崩溃
// print(name! + String(age!) + "岁")
// 方法2:使用 if let 判断
// 使用 `if let`,这种方式,表明一旦进入 if 分支,n & a 就不在是可选项
// n 和 a 的作用域仅在 `{}` 内部
// 可以使用 `,` 同时判断多个可选项是否为空
if let n = name, let a = age {
print(n + String(a) + "岁")
} else {
print("姓名或者年龄为 nil")
}
// 方法3: 使用同名参数,避免给变量再次起名的烦恼
if let name = name, let age = age {
print(name + String(age) + "岁")
}
}
5.guard
-
guard
是与if let
相反的语法,Swift 2.0 推出的
func demo4(name: String?, age: Int?) {
guard let name = name, let age = age else {
print("姓名或者年龄为 nil")
return
}
print(name + String(age) + "岁")
}
- 在程序编写时,条件检测之后的代码相对是比较复杂的
- 使用 guard 的好处
- 能够判断每一个值
- 在真正的代码逻辑部分,省略了一层嵌套
6.switch
-
switch
不再局限于整数 -
switch
可以针对任意数据类型
进行判断 - 不再需要
break
- 每一个
case
后面必须有可以执行的语句 - 要保证处理所有可能的情况,不然编译器直接报错,不处理的条件可以放在
default
分支中 - 每一个
case
中定义的变量仅在当前case
中有效,而 OC 中需要使用{}
let score = "优"
switch score {
case "优":
let name = "学生"
print(name + "80~100分")
case "良": print("70~80分")
case "中": print("60~70分")
case "差": print("不及格")
default: break
}
- switch 中能够赋值和使用
where
关键字添加赋加条件
let point = CGPoint(x: 10, y: 10)
switch point {
case let p where p.x == 0 && p.y == 0:
print("中心点")
case let p where p.x == 0:
print("Y轴")
case let p where p.y == 0:
print("X轴")
case let p where abs(p.x) == abs(p.y):
print("对角线")
default:
print("其他")
}
- 如果只希望进行条件判断,赋值部分可以省略
switch score {
case _ where score > 80: print("优")
case _ where score > 60: print("及格")
default: print("其他")
}
四.for 循环
基础循环
Range 和开闭区间
_
可以表示忽略C 风格的循环 (Swift 2.2 中过时,Swift 3.0中被舍弃)
var sum = 0
for var i = 0; i < 10; i++ {
sum += I
}
print(sum)
-
for-in
,0..<10
表示[0,10)
sum = 0
for i in 0..<10 {
sum += I
}
print(sum)
- 范围
0...10
表示[0,10]
sum = 0
for i in 0...10 {
sum += I
}
print(sum)
注意:
..<
和...
用于定义Range
类型,左右都不要添加空格
格式:
for 变量 in 范围 { // 代码 }
- 省略下标
-
_
能够匹配任意类型 -
_
表示忽略对应位置的值
-
for _ in 0...10 {
print("hello")
}
五.字符串
1.字符串演练
遍历字符串中的字符
for s in str.characters {
print(s)
}
字符串长度
let str = "hello world你好世界"
// 1. 返回指定编码的字节长度
print(str.lengthOfBytes(using: .utf8))
// 2. 返回字符个数
print(str.characters.count)
// 3. 将字符串转换成 NSString
let ocStr = str as NSString
print(ocStr.length)
字符串拼接
- 直接在 "" 中使用
\(变量名)
的方式可以快速拼接字符串
let str1 = "Hello"
let str2 = "World"
let i = 32
str = "\(i) 个 " + str1 + " " + str2
我和我的小伙伴再也不要考虑
stringWithFormat
了 :D
- 可选项的拼接
- 如果变量是可选项,拼接的结果中会有
Optional
- 为了应对强行解包存在的风险,苹果提供了
??
操作符 -
??
操作符用于检测可选项是否为nil
- 如果不是
nil
,使用当前值 - 如果是
nil
,使用后面的值替代
- 如果不是
- 如果变量是可选项,拼接的结果中会有
let str1 = "Hello"
let str2 = "World"
let i: Int? = 32
str = "\(i ?? 0) 个 " + str1 + " " + str2
- 格式化字符串
- 在实际开发中,如果需要指定字符串格式,可以使用
String(format:...)
的方式
- 在实际开发中,如果需要指定字符串格式,可以使用
let h = 8
let m = 23
let s = 9
let timeString = String(format: "%02d:%02d:%02d", arguments: [h, m, s])
let timeStr = String(format: "%02d:%02d:%02d", h, m, s)
2.String & Range 的结合
- 在 Swift 中,
String
和Range
连用时,语法结构比较复杂 - 如果不习惯 Swift 的语法,可以将字符串转换成
NSString
再处理
let helloString = "我们一起飞"
(helloString as NSString).substringWithRange(NSMakeRange(2, 3))
- 使用 Range
的写法
3.0
let startIndex = helloString.index(str.startIndex, offsetBy: 2)
let endIndex = helloString.index(str.endIndex, offsetBy: -2)
helloString.substring(with: startIndex..
4.0
let startIndex = helloString.index(str.startIndex, offsetBy: 2)
let endIndex = helloString.index(str.endIndex, offsetBy: -2)
helloString[startIndex..
六.集合
1.数组
- 数组使用
[]
定义,这一点与 OC 相同
//: [Int]
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
- 遍历
// 1> 使用下标遍历
for i in 0.. 遍历数组项
for e in numbers {
print(e)
}
print("---- 遍历数组项和索引")
// 3> 遍历数组项和索引
for e in numbers.enumerated() {
print("\(e.offset) \(e.element)")
}
print("---- 遍历数组项和索引 2")
// 4> 遍历数组项和索引 2
for (n, c) in numbers.enumerated() {
print("\(n) \(c)")
}
print("---- 倒序遍历数组")
// 5> 倒序遍历数组
for s in numbers.reversed() {
print(s)
}
print("---- 倒序遍历数组内容和下标")
// 6> 倒序遍历数组内容和下标
for (n, c) in numbers.enumerated().reversed() {
print("\(n) \(c)")
}
- 可变&不可变
-
let
定义不可变数组 -
var
定义可变数组
-
let array = ["zhangsan", "lisi"]
//: 不能向不可变数组中追加内容
//array.append("wangwu")
var array1 = ["zhangsan", "lisi"]
//: 向可变数组中追加内容
array1.append("wangwu")
- 数组的类型
- 如果初始化时,所有内容类型一致,择数组中保存的是该类型的内容
- 如果初始化时,所有内容类型不一致,择数组中保存的是
NSObject
//: array1 仅允许追加 String 类型的值
//array1.append(18)
var array2 = ["zhangsan", 18] as [Any]
//: 在 Swift 中,数字可以直接添加到集合,不需要再转换成 `NSNumber`
array2.append(100)
- 数组的定义和实例化
- 使用
:
可以只定义数组的类型 - 实例化之前不允许添加值
- 使用
[类型]()
可以实例化一个空的数组
- 使用
var array3: [String]
//: 实例化之前不允许添加值
//array3.append("laowang")
//: 实例化一个空的数组
array3 = [String]()
array3.append("laowang")
- 数组的合并
- 必须是相同类型的数组才能够合并
- 开发中,通常数组中保存的对象类型都是一样的!
array3 += array1
//: 必须是相同类型的数组才能够合并,以下两句代码都是不允许的
//array3 += array2
//array2 += array3
- 数组的删除
//: 删除指定位置的元素
array3.removeAtIndex(3)
//: 清空数组
array3.removeAll()
- 内存分配
- 如果向数组中追加元素,超过了容量,系统会增加合适的容量
var list = [Int]()
for i in 0...16 {
list.append(i)
print("添加 \(i) 容量 \(list.capacity)")
}
2.字典
- 定义
- 同样使用
[]
定义字典 -
let
不可变字典 -
var
可变字典 -
[String : Any]
是最常用的字典类型
- 同样使用
//: [String : Any] 是最常用的字典类型
var dict = ["name": "zhangsan", "age": 18]
- 赋值
- 赋值直接使用
dict[key] = value
格式 - 如果 key 不存在,会设置新值
- 如果 key 存在,会覆盖现有值
- 赋值直接使用
//: * 如果 key 不存在,会设置新值
dict["title"] = "boss"
//: * 如果 key 存在,会覆盖现有值
dict["name"] = "lisi"
- 删除一个 KEY
// 删除 key 如果不存在,什么也不会发生
dict.removeValue(forKey: "height")
print(dict)
// 另一种删除方式
dict["title"] = nil
print(dict)
- 遍历
-
k
,v
可以随便写 - 前面的是
key
- 后面的是
value
-
//: 遍历
for (k, v) in dict {
print("\(k) ~~~ \(v)")
}
- 合并字典
- 如果 key 不存在,会建立新值,否则会覆盖现有值
//: 合并字典
var dict1 = [String: Any]()
dict1["nickname"] = "大老虎"
dict1["age"] = 100
//: 如果 key 不存在,会建立新值,否则会覆盖现有值
for (k, v) in dict1 {
dict[k] = v
}
print(dict)
七.错误处理
反序列化JSON数据
- 网络访问代码
/// Swift 4 中 字符串换行的写法
let data = """
{
"weatherinfo": {
"city": "北京",
"cityid": "101010100",
"temp": "18",
"WD": "东南风",
"WS": "1级",
"SD": "17%",
"WSE": "1",
"time": "17:05",
"isRadar": "1",
"Radar": "JC_RADAR_AZ9010_JB",
"njd": "暂无实况",
"qy": "1011",
"rain": "0"
}
}
""".data(using: String.Encoding.utf8)!
- JSON 反序列化
// 方式1:强try.加载失败直接崩溃
let dict = try! JSONSerialization.jsonObject(with: data!, options: [])
print(dict)
// 方式2:可选try.反序列化失败返回nil
let dict = try? JSONSerialization.jsonObject(with: data!, options: [])
print(dict)
// 方式3:默认try.返回序列化失败可以捕捉详细信息
do {
let dict = try JSONSerialization.jsonObject(with: data!, options: [])
print(dict)
}catch {
print(error)
}