简介
Swift的学习笔记
以下代码可以直接在playground中运行
学习版本:swift 4.2
Hello World
1 | print("Hello, world!") |
关键点
Simple Values
let
,常量声明var
,变量声明,常量可以先声明,随后在使用前初始化一次
总结
- swift是强类型的,一旦变量类型被确定,就不能改变
- 不需要手动声明类型,通过初次赋值,编译器可以推断出变量/常量的类型
- 当定义不足以提供所需的数据类型时,可以使用显示的类型定义
1
2
3let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70 - 不支持隐式转换,所有不同类型的赋值都需要显示的转换
1
2
3
4let label = "The width is "
let width = 94
let widthLabel = label + String(width)
print(widthLabel) - 可以使用
\(内容)
的方式在字符串中插入其他变量的内容1
2
3
4
5
6
7
8
9
10
11
12
13let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruits."
let a = "test"
let b = "This is a \(a)."
print(appleSummary)
print(fruitSummary)
print(b, b)
let c = 2.5
let d = 1.23333
let e = "This is a \(c + d + 3.1 + 10)."
print(e) - 使用三个双引号包围可以创建多行字符串,同时可以使用特殊字符而不需要转义(替换变量/常量功能依然有效)
1
2
3
4let quotation = """
I said "I have \(apples) apples."
And then I said "I have \(apples + oranges) pieces of fruit."
""" - 使用中括号
[]
来定义/声明数组和字典,使用索引或key来访问/修改元素,最后一个元素结尾可以有,
1
2
3
4
5
6
7
8var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations" - 创建空数组或字典,需要使用初始化语法
1
2let emptyArray = [String]()
let emptyDictionary = [String: Float]() - 如果数组或字典的类型可以被编译器推断出来则可以直接使用
[]
,[:]
来初始化数组和字典.(不知道怎么使用,据说是传参数或者设置变量时可以这样用)1
2shoppingList = []
occupations = [:]
Control Flow
简介
if
/switch
,条件控制for-in
/while
/repeat-while
,循环- 条件的
()
/循环变量
可以省略 - 执行代码的
{}
不能省略
1 | let individualScores = [75, 43, 103, 87, 12] |
if
判断条件并执行代码
- 条件必须是bool类型
- 不会隐式和0做比较
- 在类型之后使用
?
来表示该变量为可选变量(可以有值或为nil
) - 可以使用
if
+let
判断可选参数是否为空1
2
3
4
5
6var a:Bool? = false
if let a = b {
print(1, a) // 会进入这个条件
} else {
print(2)
}1
2
3
4
5
6
7
8var optionalString: String? = "Hello"
print(optionalString == nil)
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
} - 可以使用
??
来为可选变量设置默认值1
2
3
4let nickName: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickName ?? fullName)"
print(informalGreeting)
switch
- 支持比较判断
- case支持赋值函数判断
- case内不需要break
- case支持使用
,
来表示或操作 - 必须有default语句(所有的case包含了所有情况的话,则不需要default)
1 | let vegetable = "red pepper" |
for-in
1 | let interestingNumbers = [ |
for
..<
,遍历不包含上限...
,遍历包含上限
1 | var total = 0 |
while
1 | var n = 2 |
Functions and Closures
func
函数调用的参数有一个标签的概念,
调用函数需要指定参数的标签进行调用.
默认情况下,是使用参数名来作为标签.
可以使用_
来表示不使用标签,
可以在参数名前增加标签的名字(用空格分隔)
当添加标签后,原始的参数名将不能作为函数参数标签使用
(标签并不能改变参数的调用顺序,还是需要从前向后一次传参)
默认标签的使用
1 | func greet(person: String, day: String) -> String { |
标签的常规调用
1 | func greet(_ person: String, on day: String) -> String { |
函数的多返回
- 返回值使用tuple的方式返回
- 获取返回值的内容既可以使用返回的参数名,又可以使用索引
1 | func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) { |
局部函数
局部函数可以使用外部的变量
可以用于实现一些复杂的问题
1 | func returnFifteen() -> Int { |
函数可以作为参数,返回值
1 | func a() -> ((Int) -> Int) { |
1 | func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool { |
匿名函数
1 | func a(i : Int, f : (Int) -> Int) { |
匿名函数的参数和返回值可以推断省略
单行函数,会直接返回这行代码执行的结果
1 | var a = [1, 2, 3] |
匿名函数中可以使用从0开始的序号来代替参数名
1 | var a = [3, 1, 2] |
Objects and Classes
- 类的声明
1
2
3
4
5
6class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
} - 类的使用
1
2
3
4var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
print(shapeDescription) - 构造函数(init),类属性都必需要初始化,要么在声明中初始化,要么在构造函数中初始化
- 同名属性和参数使用
self
区分 - 不能使用
func
做关键字1
2
3
4
5
6
7
8
9
10
11
12class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
- 同名属性和参数使用
- 析构函数(deinit),在类生命终止时,需要做的操作
- 子类
- 不强制继承某个类
- 调用基类函数使用
super
关键字 - 覆盖基类函数强制使用
override
关键字
- 属性的setter/getter
set
默认使用newValue
作为参数名(可以手动设置该参数名)- 有
get
/set
的属性必然是计算属性(不是值属性)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18class Shape {
var a = 1
var b : Int {
get {
return a * 2
}
set {
a = newValue / 2
}
}
}
var a = Shape()
print(a.a)
print(a.b)
a.b = 4
print(a.a)
print(a.b) willSet
/didSet
方法,就是在设置值属性前/后调用的方法,必然是值属性(不是计算属性)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Shape {
var a = 1
var b : Int {
willSet {
print("will set")
}
didSet {
print("did set")
}
}
init() {
b = 10
}
}
var a = Shape()
print(a.a)
print(a.b)
a.b = 4
print(a.a)
print(a.b)
- 可选值的相关使用,如果前面的指针是nil,则后面的内容都不会执行,表达式返回nil
1
2
3
4
5
6
7
8class A {
var a = 10
}
// var a: A? = A()
var a: A? = nil
var b = a?.a
print(b)
Enumerations and Structures
枚举
- 默认从0开始
- 递增1
- 可以有函数
- 类型可以有字符串,浮点数等
- 使用
rawValue
来访问具体对应的数值1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23enum Rank : Int {
case a = 1
case b, c, d, e
case f, g, h
func simpleDescription() -> String {
switch self {
case .a:
return ":a"
case .b:
return ":b"
case .f:
return ":f"
default:
return String(self.rawValue)
}
}
}
var a = Rank.a
var aRawValue = a.rawValue
print(a)
print(aRawValue)
print(a.simpleDescription()) - 在枚举外部,调用枚举时需要使用
类型.枚举
- 当使用
switch
限定了参数的类型时,调用枚举时,只需要使用.枚举
即可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
26enum Suit {
case spades, hearts, diamonds, clubs
func simpleDescription() -> String {
switch self {
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
func color() -> String {
switch self {
case .spades, .clubs:
return "black"
case .hearts, .diamonds:
return "red"
}
}
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
print(hearts.color()) - 相同枚举的实例的值一般是相同的
- 奇怪的代码,主要是不知道这样写的意义是什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}
结构体
- 结构体和类基本功能一样
- 区别
- 传参的时候,结构体是值传递(完全拷贝),类是引用传递
1
2
3
4
5
6
7
8
9struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
- 传参的时候,结构体是值传递(完全拷贝),类是引用传递
Protocols and Extensions
Protocol
protocol
就是interface
.- swift是单继承,多实现的方式,只能继承一个类,但是可以同时实现多个接口(protocol),使用
,
分隔 - 使用
protocol
关键字声明协议1
2
3
4protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
} - 类,枚举和结构体都可以继承protocol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription mutating
关键字用于表示该函数会修改成员属性class
内部不需要使用该关键字,因为类函数都是修改类内容的
1 | protocol AProtocol { |
extension
- 可以为已有类型追加函数
1
2
3
4
5
6
7
8
9extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
var a = 10
a.adjust()
print(a)
print(a.simpleDescription)
Error Handling
定义
- 错误是枚举类型
- 需要实现
Error
接口
1 | enum PrinterError: Error { |
抛出异常
- 在可能抛出一样的函数声明时需要在参数列表后面加
throws
标记,标识函数可能抛出异常 - 使用
throw
和对应的异常枚举来抛出异常 - 函数在抛出异常后会立刻返回,终止执行,不会有返回值
1 | func send(job: Int, toPrinter printerName: String) throws -> String { |
捕获异常
- 使用
do...catch
来捕获异常 catch
中的异常默认名为error
,可以手动设置来修改该名称- 调用可能会抛出异常的函数时需要在函数名前使用
try
- 假如没有发生异常,则
catch
代码段不会执行
1 | do { |
- 可以使用多个
catch
,分别捕获不同的异常
1 | do { |
- 可以使用
try?
来忽略异常,发生异常后,返回nil,无异常则正常返回
1 | let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler") |
例子
1 | enum PrinterError: Error { |
defer
- 在代码块内所有代码执行后,最后执行的代码
- 不会因为有异常而不执行
1 | var fridgeIsOpen = false |
Generics
- 就是像c++模版一样的功能
1 | func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] { |
- 类/枚举也可以使用模版
1 | // Reimplement the Swift standard library's optional type |
where
可以限定模版参数的类型- Writing
<T: Equatable>
is the same as writing<T> ... where T: Equatable
.
1 | func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool |
1 | func anyCommonElements<T: Sequence>(_ lhs: T, _ rhs: T) -> Bool |