Swift语法基础01
本系列是Swift编程语言的学习笔记,主要根据Swift官方文档的内容,然后加上自己的理解整理而来。 今天的内容主要包含四部分:
-
1. 精选Swift语言学习资源推荐
-
2. Swift语言简介
-
3. Swift开发语言环境搭建
-
4. Swift语言基础部分
精选Swift语言学习资源推荐
如下是整理的一些不错的学习Swift的资源
-
• Swift官网
-
• Swift官方文档中文版
-
• 官方Swift快速入门指南
-
• 官方Swift Book
-
• Swift学习官方学习资源(含文档,视频,示例代码等)
-
• Hacking With Swift
-
• Swift代码格式化工具SwiftFormat
-
• Swift Extension for Visual Studio Code
Swift语言介绍
如果想学习iOS开发,前提就是要掌握Swift
或Object-C
语言。就像如果要学习Android开发的话,必须先掌握 Java
Kotlin
语言一样。
Switf语言发展十分快,从2014年发布以来,到现在已经发布到了5.6.2
版本。并且由于苹果公司一直在主推Swift
语言,希望它能逐步取代Object-C
, 所以目前如果要学习iOS开发的话,选择了Swift
语言入手比Object-C
更合适。
Swift语言在2015年被开源后,单独放了一个网站上面,与开发iOS不同,如果只是学习Swift语言本身的话,是不需要mac电脑的。Swift
作为一门跨平台语言,不仅可以在mac系统上运行,也可以在Linux, Windows上运行。不仅能开发iOS App, 也能做服务端开发。学会一门语言,就能够达到跨平台,完成移动端和服务端开发,是不是很划算呢?😁
开发环境搭建
安装Swift
根据自己的操作系统,在下载页面选择合适的安装包安装即可。
以macOS
为例,只需要在App Store商店里安装Xcode
即可。
安装好以后,在终端下执行如下命令即可检查是否安装成功。
$ swift --version
swift-driver version: 1.45.2 Apple Swift version 5.6.1 (swiftlang-5.6.0.323.66 clang-1316.0.20.12)
Target: x86_64-apple-macosx12.0
当前学习使用的是Swift 5.6.1
版本,后面所有的例子也会基于这个版本。
交互模式
在终端直接输入swift
,即可进入交互模式,在这里可以快速输入一些Swift语句测试效果,非常实用。
$ swift
Apple Swift version 5.6.1 (swiftlang-5.6.0.323.66 clang-1316.0.20.12)
Target: x86_64-apple-macosx12.0
Welcome to Swift!
Subcommands:
swift build Build Swift packages
swift package Create and work on packages
swift run Run a program from a package
swift test Run package tests
swift repl Experiment with Swift code interactively (default)
Use `swift --help` for descriptions of available options and flags.
Use `swift help <subcommand>` for more information about a subcommand.
Welcome to Apple Swift version 5.6.1 (swiftlang-5.6.0.323.66 clang-1316.0.20.12).
Type :help for assistance.
执行Swift语句
1> print("Hello World")
Hello World
基础部分
常量和变量
在Swift语言中,let
关键字和var
关键字分别用来表示常量和变量。
var str: String = "Hello, Swift"
// 当类型能够由赋值自动推导时,类型声明可以省略
let a = "Hello, Swift"
Swift是一种类型安全语言,即常量或者变量在声明的时候必须指定明确的类型或者是可以直接推导的类型。
Swift语言也支持在同一行语句中声明多个常量或者变量。
var x = 0.0, y = 0.0, z = 0.0
如果在同一行代码中声明多个变量并且都没有提供初始值,可以通过指定最后一个变量的类型对整体进行类型指定。
var red, green, blue: Double
更多用法如下:
// 常量let
let maxNum = 100
// 变量var
var index = 2
var str = "Hello, playground"
// 一次定义多个变量
var x=1, y=2, c=3
// 常量在编译的时候才能确定它的值,但是只能赋值一次,不允许再修改
// 如下面的常量c
let a = 1000
let b = 50
let c = a + b
// 指定常量类型
let b: String = "xxx"
// 批量指定变量类型
var j, k, l: Double
let label = "The width is "
let width = 94
// 使用三引号(""")可以起到一次使用跨行声明变量
let a = """
line one
line two
"""
变量和常量命名规范
Swift的变量名可以是任何的Unicode字符, 如: 中文, Emoji表情等都可以是变量名。
常量与变量名不能包含空格符,数学符号,箭头,保留的(或者非法的)Unicode 码位,连线与制表符。也不能以数字开头, 但是可以在常量与变量名的其他地方包含数字。
let π = 3.14159
let 你好 = "你好世界"
let 🐶🐮 = "dogcow”
驼峰命名方式
Swift语言官方文档采用驼峰命名的方式。所谓驼峰命名,是指以单词进行名称的拼接,名称的首字母一般为小写,之后每个单词的首字母大写,其他字母均小写,
Swift中的命名也有一些约定俗成的规则,例如量值属性首字母会小写,类名、枚举名、结构体名首字母会大写。
如果在命名中真的需要使用预留的关键字进行命名,可使用反引号
符号进行包装,但是除非万不得已,开发中尽量不要使用这种方式命名。
var `var`="hello"
print(`var`)
打印常量和变量
使用print
函数,可以打印变量和常量
let x=1 ,y = 2, z=3
let name = "John"
print(x, y, z) // 可以一次传入多个参数
print(x, y, z, separator: ":") // 指定分隔符
print(x, y, z, separator: ":", terminator: ";") //指定行尾截止符号,默认是回车
print("My name is (name)") // 变量替换
分号
Swift语法中,每行代码结尾的分号是可以省略的,但是如果一行包含多个语句时,分号则不能省略
let cat = 'cat'; print(cat)
注释
Swift语言采用和C语言类似的注释方式,使用//
符号来注释单行内容,同时也可以使用以/*
开头、以*/
结尾的方式进行多行注释,
Swift语言的注释还有一个十分有趣的特性,即可以进行注释的嵌套
// 单行注释
/*
多行注释
*/
整型
学习整型之前,先了解下位和字节
位和字节
计算机内存中最小的数据运算单元是一个二进制位(bit
),其只有两种状态:0
或者1
。字节(B
)是最小的数据单元,1字节由8个二进制运算位组成。 它们之间的换算关系如下:
1B=8bit
1KB=2^10B
1MB=2^10KB
1GB=2^10MB
1TB=2^10GB
1PB=2^10TB
整型分类
Swift中的整型数据分为有符号数和无符号数两类。
有符号数类型有8位(Int8
)、16位(Int16
)、32位(Int32
)和64位(Int64
)。
一般建议直接使用Int
类型,它的位数默认与当前使用的操作平台相关:
-
• 在32位的平台上,等效于
Int32
-
• 在64位的平台上,等效于
Int64
无符号数UInt
的用法也是跟前面一样的。
使用MemoryLayout
枚举的size
属性可以获取数据类型占用的字节数
// 输出为8,表示Int占用了8字节,当前为64位的平台
print(MemoryLayout<Int>.size) // 8
如下是一些整型的使用方式
// 有符号数最大值和最小值
Int.max
Int.min
// 无符号数最大值和最小值
UInt.max
Uint.min
// 指定机器位数
Int8.max
Int8.min
// 十进制
let decimalInteger = 17
// 二进制的17 “0b”
let binaryInteger = 0b10001
// 八进制的17 "0o"
let octalInteger = 0o21
// 十六进制的17 "0x"
let hexadecimalInteger = 0x11
浮点型
浮点型分为单精度和双精度浮点型,分别用Float
和Double
表示
// 单精度和双精度浮点数
var a: Float = 3.1415926
var b: Double = 3.1415926
Float
占用32位,Double
占用64位. Double
类型比Float
类型有着更高的精度,除了某些特殊场景外,更推荐使用Double
类型来定义浮点数。
print(MemoryLayout<Float>.size) // 4 字节
print(MemoryLayout<Double>.size) // 8 字节
Swift语言支持使用科学计数法来表示数字,在十进制中,使用e
来表示10的n
次方,在十六进制中,使用p
来表示2的n
次方
var a = 1.25e3 // 1.25 *(10^3)
var b = 0x1p3 // 1*(2^3)
print(a) // 1250
print(b) // 8
整型和浮点型美化
Swift语言中还有一个十分有意思的特性,无论是整型数据还是浮点型数据,都可以在数字前加任意个0来进行位数填充,也可以在数字中加入下画线进行分隔,进而增加可读性,这些操作并不会影响原始数值,却提高了对开发者编程的友好性,使代码的结构更加清爽。
示例如下:
let a = 001.123 // 1.23
let b = 1_000_000 // 1000000 一百万
let c = 1_000.1_001 // 1000.1001
布尔类型
swift中bool类型只有true
和false
两个值,不会对零值,空值做布尔转换。
// bool值都是小写
let imTrue: Bool = true
let imFalse: Bool = false
if imTrue {
print("I am True")
}
else if 3+4 == 7{
print("3+4 == 7")
}
else{
print("I am false")
}
// 不能用非bool类型的值作为判断条件,如下面的用法会报编译错误
if 1 {
print("I am true")
}
元组
元组是Swift语言中重要的数据类型之一,元组允许一些并不相关的类型自由组合成为新的集合类型。
-
• 将多个不同的值集合成一个数据
-
• 可以有任意多个值
-
• 不同的值可以是不同的类型
var pen:(name:String,price:Int) = ("钢笔",2)
上面的代码在创建元组类型的同时指定了其中参数的名称,即名称参数为name
,价格参数为price
,开发者可以使用这些参数名称来获取元组中各个参数的值,示例如下:
//获取pen变量名称
var name = pen.name
//获取pen变量价格
var price = pen.price
开发者在创建元组时,也可以不指定元组中参数的名称,元组会自动为每个参数分配下标,下标值将从0
开始依次递增,示例如下:
//不指定参数名称的元组
var car: (String,Int) = ("奔驰",2000000)
//通过下标来取得元组中各个组成元素的值
var carName = car.0
var carPrice = car.1
元组实例被创建后,开发者也可以通过指定的变量或者常量来分解它,分解后的变量必须与元组中的元素一一对应(个数相等)
var car: (String,Int) = ("奔驰",2000000)
var (theName, thePrice) = car
print(theName, thePrice)
开发者可能并不需要获取某个元组实例中所有元素的值,这种情况下,开发者也可以将某些不需要获取的元素使用匿名的方式来接收。
在Swift语言中,常常使用符号_
来表示匿名的概念,因此_
也被称为匿名标识符
var logingResult = (true, "xxxxx")
// 忽略第二个值
let (isLoginSuccess, _) = logingResult
更多元组的使用示例如下
var point = (5,2)
var httpResponse = (404, "Not Found")
var point2: (Int, Int, Int) = (1,2,3)
// 元组赋值
var point = (5,2)
// 与python中不一样,赋值时等号左边的圆括号不能省略
let (x, y) = point
let (status_code, status_message) = http404Error
print(x)
print(y)
// 通过下标访问元组元素,注意这里通过的是".index", 而不是"[index]"
print(point.0)
print(point.1)
// 通过元素名称访问
var point3 = (x: 1, y: 2)
point3.x
point3.y
// 声明时指定元素名称
var point4: (x: Int, y: Int) = (2,3)
point4.x
point4.y
// 使用"_"忽略一些元素
var logingResult = (true, "xxxxx")
let (isLoginSuccess, _) = logingResult
isLoginSuccess
元组虽然使用起来十分方便,但是其只适用于简单数据的组合,对于结构复杂的数据,要采用结构体或者类来实现。
可选值(Optional)
在Swift语言中,如果使用了一个没有赋值的变量,程序会直接报错并停止运行。
在Swift语言中,未初始化的普通类型是不允许使用的,哪怕是用来进行判端是否为空也不被允许,当然也就不可以与nil
进行比较运算,这种机制极大地减小了代码的不可控性。因此,开发者在使用前必须保证变量被初始化。
// 如下面的写法会报错,因为a没有初始化
var a: String
if (a == nil) {
}
正确的做法是使用可选类型
//使用可选值
var a: String?
if (a == nil) {
}
可选值是对普通类型的一种包装。在判断其值不为nil
之后,要使用它的值,需要用!
来拆包。
var a: String?
if (a == nil) {
// a! 拆包获取a变量的值
print(a!)
}
当使用可选值时,可以写个?
在运行符前面,如果?
前面的值是nil
, 所有在?
后面的值都是nil
,
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
Swift语言还提供了一种if-let
语法结构来进行Optional类型值的绑定操作,可以将上面的结构改写如下:
var a: String? = "abc"
// a不为nil时,将a赋值给临时变量tmp
if let tmp = a {
print(tmp)
} else {
print("a is nil")
}
if
语句赋值的变量tmp
只在if
语句范围内有效。
如果原来的变量名不再需要的话,可以使用同样的变量名
let myNumber = Int(possibleNumber)
// 使用同样的名字拆包
if let myNumber = myNumber {
print("My number is (myNumber)")
}
// Prints "My number is 123"
上面的还可以简化为
if let myNumber {
print("My number is (myNumber)")
}
// Prints "My number is 123"
if-let
结构中也可以同时进行多个Optional
类型值的绑定,之间用逗号隔开。 在同时进行多个Optional类型值的绑定时,只有所有Optional值都不为nil
,绑定才会成功,代码执行才会进入if
为真的代码块中
如果开发者需要在if
语句的判断中添加更多业务逻辑,可以通过追加子句的方式来实现,如下面的tmp1 < tmp2
。
var obj1: Int? = 1
var obj2: Int? = 2
if let tmp1 = obj1, let tmp2 = obj2, tmp1 < tmp2 {
print(tmp1, tmp2)
}
Swift中还有一种语法:隐式解析。隐式解析适用于这样的场景:当我们明确某个变量初始时为nil
,并且在之后使用之前一定会被赋值时,我们可以将其声明为隐式解析的可选值,再对这个变量进行使用,就不需要进行拆包操作了。
var obj4:Int?
obj4 = 3
print(obj4 + 1) //会编译异常,因为obj4没有进行拆包
如果将上面的代码做如下的修改,就可以正常运行了:
// 声明obj4为隐式解析的变量
var obj4:Int!
obj4 = 3 // 在使用时,无须再进行拆包操作,Swift会自动帮我们拆包
print(obj4 + 1)
类型别名
可以使用 typealias
关键字来定义类型别名
typealias AudioSample = UInt16
//AudioSample.min 实际上是 UInt16.min ,所以会给 maxAmplitudeFound 赋一个初值 0
var maxAmplitudeFound = AudioSample.min
在实际开发中,灵活使用typealias
为类型取别名可以优化代码的可读性。
异常处理
可以通过适配Error
的protocol来定义错误, 通过throw
抛出异常,使用throws
标记可能产生异常的方法。
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
case unKown
}
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
} else if printerName == "outOfPaper" {
throw PrinterError.outOfPaper
} else if printerName == "OnFire" {
throw PrinterError.onFire
} else if printerName == "UnKown" {
throw PrinterError.unKown
}
return "job sent"
}
有几种方式处理异常,一种是使用do-catch
, try
写在可能产生异常的代码前面,catch
代码块里,如果不指定名字的话,默认用error
代表错误。
do {
let printerResponse = try send(job:1040, toPrinter: "Never Has Toner")
print(printerResponse)
} catch {
print(error)
}
do {
let printerResponse = try send(job:1440, toPrinter: "UnKown")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: (printerError)")
} catch {
print(error)
}
另一种处理异常的方式是使用try?
。把结果转变为可选值,当有异常出现的时候,异常会被直接忽略,并把结果赋值为nil
.
let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
使用defer
,可以在函数返回前,执行一段代码,无论函数是正常执行或抛出异常,可以在多个函数之间使用defer
写一些初始化或清理工作的代码。
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
print("call defer")
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
fridgeContains("banana")
print(fridgeIsOpen)
断言(Assertions)和先决条件(Preconditons)
Assertions和Preconditions的区别在于Assertions只在debug build中生效, 而Precondtions在debug build和production build中都生效。
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
//省略断言消息
assert(age >= 0)
//使用assertionFailure直接抛出断言异常
if age > 10 {
print("You can ride the rollercoaster or the ferris wheel.")
} else if age > 0{
print("You can ride the ferris wheel.")
}
else {
assertionFailure("A person's age can't be less than zero.")
}
var index = 0
precondition(index > 0, "Index must greater than zero.")
//使用preconditionFailure直接抛出异常
var age = 0
if age > 10 {
print("You can ride the rollercoaster or the ferris wheel.")
} else if age > 0{
print("You can ride the ferris wheel.")
}
else {
preconditionFailure("A person's age can't be less than zero.")
}
PS:
当在编译的时候用的是unchecked mode
(-Ounckecked
), precondition是不会被执行的,编译器会默认所有的preconditions的条件都是成立的(返回true
)。
今天学到的内容已经足够多了,让我们稍微休息一下,明天继续!
发表评论