swift学习笔记
参考教程
https://www.runoob.com/swift/swift-data-types.html
swift代码规范
https://juejin.cn/post/7129465308376465422
1 环境搭建
必须要有苹果电脑且安装Xcode
2 基本语法
Swift是类型安全的语言,编译时会进行类型检查
import Cocoa
var mySting = "Hello, World!"
print(myString)
2.1 引入
可以使用import语句引入任何的Objective-C(C语言的严格超集)
- 创建macOS playground则引入Cocoa,Cocoa由Objective-C语言写成,因此可以混入C语言代码,甚至C++代码
- 创建iOS playground则引入UIKit
2.2 注释
// 这是单行注释(单行注释以//开头)
/* 多行注释
多行注释(以/*开始,以*/结束,可以嵌套单行注释)*/
2.3 分号
Swift不要求每行结尾使用分号,但在同一行书写多条语句必须用分号隔开
import Cocoa
var myString = "Hello, World!"; print(myString)
2.4 标识符
标识符是给变量、常量、方法、函数、枚举、结构体、类、协议等指定的名字
- 区分大小写
- 可以以字母或下划线开头,但不能以数字开头
- 标识符中的其他字符可以是字母、数字或下划线,不能是关键字
Swift使用Unicode编码,可以使用中文,甚至聊天表情符
import Cocoa
// 如果一定要使用关键字作标识符,可以在关键字前后加`
let `class` = "Runoob"
2.5 关键字
2.6 空格
Swift语言并不是像C/C++,Java那样完全忽视空格,Swift对空格的使用有一定的要求,但是又不像Python对缩进的要求那么严格。在Swift中,运算符不能直接跟在变量或常量的后面。
import Cocoa
// 错误1:let a= 1+ 2
// 错误2:let a = 1+ 2
let a = 1 + 2
let b = 3+4 // 这样写是可以的
为避免错误,尽量严格缩进
2.7 打印输出
import Cocoa
print("Runoob") // 使用print函数输出,如果多个print,默认换行输出
// 如果不想换行则将terminator设置为空字符串,这样每个输出以空格分隔
for x in 0...10 { // 这是循环语句,后续会介绍
print("\(x)", terminator: "") // \()可以将x变为字符串
}
// 如果接收用户的输入使用readLine()
let theInput = readLine()
3 数据类型
3.1 内置数据类型
- Int:整型,在32位平台上和Int32长度相同,在64位平台上和Int64长度相同
- UInt:无符号整型,在32位平台上和UInt32长度相同,在64位平台上和UInt64长度相同(尽量不要使用UInt,统一使用Int可以提高代码的可复用性)
- Float:32位浮点数,精度较低,至少有6位数字
- Double:64位浮点数,精度很高,至少有15位数字
- Bool:布尔值,有true、false和nil(表示没有值)
- String:字符串,是字符的序列集合,用双引号引起来
- Character:字符,单个字母,用双引号引起来
- Optional:可选类型,表示有值或没有值
3.1.1 字符串
1.字符串的创建:使用字符串字面量或String类实例子创建字符串
import Cocoa
// 使用字符串字面量创建字符串
var stringA = "Hello, World"
print(stringA)
// 使用String类的实例创建字符串
var stringB = String("Hello, World!")
print(stringB)
2.空字符串,可以使用字符串属性isEmpty判断字符串是否为空
import Cocoa
// 使用字符串字面量创建空字符串
var stringA = ""
if stringA.isEmpty {
print("stringA是空的")
} else {
print("stringA不是空的")
}
// 使用String类的实例创建空字符串
var stringB = String()
if stringB.isEmpty {
print("stringB是空的")
} else {
print("stringB不是空的")
}
3.字符串插值:在字符串中使用反斜线和括号插入变量
import Cocoa
var age: Int = 24
print("My age is \(age)")
4.字符串连接:使用加号连接
import Cocoa
let constA = "Hello, "
let constB = "World!"
var stringComb = constA + constB
print(stringComb)
5.字符串长度:使用count属性计算
import Cocoa
var varA = "Hello, World!"
print("\(varA)的长度为\(varA.count)") // 输出为13
6.字符串比较:使用==比较两个字符串是否相同
7.Unicode字符串:String是基于Unicode建立的,可以循环迭代出字符串中的UTF-8和UTF-16的编码
import Cocoa
var unicodeString = "Hello, World!"
for code in unicodeString.utf8 {
print(code)
}
for code in unicodeString.utf16 {
print(code)
}
8.字符串分割成数组
import Cocoa
let fullName = "First Last"
let fullNameArr = fullName.split{" "}.map(String.init)
fullNameArr[0] // First
fullNameArr[1] // Last
3.1.2 字符
1.空字符:swift不能创建空字符常量或变量
2.遍历字符串中的字符
import Cocoa
for ch in "Hello, World!" {
print(ch)
}
3.字符串连接字符:使用String的append()方法
import Cocoa
var varA: String = "Hell"
var varB: Character = "o"
varA.append(varB)
print(varA)
3.2 类型别名
// 使用typealias关键字定义
import Cocoa
typealias Feet = Int // 定义类型别名
var distance: Feet = 100
print(distance)
3.3 类型推断
如果没有显示的制定类型,Swift会根据初始值进行类型推断
import Cocoa
let meaningOfLife = 42 // meaningOfLife被推断为Int类型
let pi = 3.1415 // pi被推断为Double类型,而非Float类型
let anotherPi = 3 + 0.1415 // anotherPi被推断为Double类型
4 变量
4.1 变量声明
使用var关键字来声明
import Cocoa
// 方式一:
var varA: Int
varA = 42
// 方式二:
var varB: Double = 3.14
4.2 变量命名
- 变量名可以由字母、数字和下划线组成
- 变量名需要以字母或下划线开始
- 变量名区分大小写
- 变量名可以使用Unicode
import Cocoa
var 你好 = “你好”
print(你好)
4.3 变量输出
- 变量和常量使用print函数输出
- 字符串差值输出:在字符串中使用反斜线和括号插入变量
import Cocoa
var age: Int = 24
print("My age is \(age)")
5 可选类型
Optional:可选类型,用于处理值缺失情况,表示可能有或可能没有值
当声明一个可选变量或者可选属性时没有提供初始值,则默认为nil
// 声明方式
// 方式一:
var optionalInteger1: Int? // 类型和?之间没有空格
// 方式二:
var optionalIneger2: Optional<Int>
// 强制解析:如果一个可选类型的实例包含一个值,可以用后缀!来访问这个值,如果可选类型为nil,使用!运行时会报错
optionalInteger1 = 42
print(optionalInteger1!) // 强制解析
// 自动解析:在声明可选变量时使用!替代?,后续再获取该值时不需要!强制解析
var myString: String!
myString = "Hello, World!"
print(myString)
可选绑定:判断可选类型是否包含值,如果包含就把值赋给一个临时常量或变量,用在if和while语句
import Cocoa
var myString: String?
myString = "Hello World!"
if let yourString = myString {
print("你的字符串的值为:\(yourString)")
} else {
print("你的字符串没有值")
}
6 常量
6.1 常量声明
常量类似于变量,区别在于常量的值一旦设定就不能改变,而变量的值可以随意更改,使用let关键字来声明
import Cocoa
let constA = 42
print(constA)
6.2 类型标注
import Cocoa
let constB: Double = 3.14
print(constB)
6.3 常量命名
和变量的命名规则相同,同4.2
6.4 常量输出
和变量的输出方式相同,同4.3
7 字面量
7.1 整型字面量
二进制前缀为0b,八进制前缀为0o,十六进制前缀为0x,十进制没有前缀
let decimalInteger = 17 // 十进制17
let binaryInteger = 0b100001 // 二进制17
let octalInteger = 0o21 // 八进制17
let hexadecimal = 0x11 // 十六进制17
7.2 浮点型字面量
let decimalDouble = 12.1e2 // 十进制浮点型字面量:12.1*10^2
let hexadecimalDouble = 0xC.3p3 // 十六进制浮点型字面量:12.3*2^3
7.3 字符串型字面量
字符串型字面量中不能包含未转义的引号,未转义的反斜线、回车符或换行符
7.4 布尔型字面量
- true表示真
- false表示假
- nil表示没有值
8 运算符
8.1 算术运算符
+、-、*、/、%
Swift中已经取消了++和–
8.2 比较运算符
==、!=、>、<、>=、<=
8.3 逻辑运算符
- &&:逻辑与
- ||:逻辑或
- !:逻辑非
8.4 位运算符
对二进制位进行操作
- &:按位与
- |:按位或
- ^:按位异或,输入数同一位不同为1,相同为0
- ~:按位取反
- <<:按位左移,左移后左边的数位被丢弃,右边的空位用0填充
>>
:按位右移,右移后右边的数位被丢弃,左边的空位用0填充
8.5 赋值运算符
=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
8.6 区间运算符
- a…b:闭区间运算符,定义一个从a到b(包括a和b)所有值的区间
- a…<b:开区间运算符,定义一个从a到b但不包含b的所有值的区间
8.7 其他运算符
- a ? b : c 三目运算符,如果a为true值为b,否则为c
- ?? 合并空值运算符,例如a ?? b,如果a有值则展开,如果没有值(nil),则返回b。其中表达式a必须是可选类型,表达式b必须与a的存储类型相同
8.8 运算符优先级
原则
- 指针最优,单目运算符优于双目运算符
- 先乘除后加减
- 先算数运算符,后移位运算符,最后位运算符
- 逻辑运算符最后计算
9 条件语句
10 循环语句
11 数组
如果创建一个数组,并赋值给一个变量,则创建的集合可添加、删除、修改,如果赋给常量则数组不可更改
11.1 创建数组
import Cocoa
// 1. 创建Int类型的空数组变量
// 法一:(推荐)
var someInts1: [Int] = []
// 法二
var someInts2 = [Int]()
// 2. 创建数量为3,初始值为0的Int类型数组
var someInts3 = [Int](repeating: 0, count: 3)
// 3. 创建含有3个元素的数组
var someInts4: [Int] = [10, 20, 30] // 必须用逗号隔开,不能只用空格隔开
11.2 访问数组
// 通过索引获取数组值:索引下标从0开始
import Cocoa
var someInts = [Int](repeating: 10, count: 3)
var someVar: Int = someInts[0]
print(someVar)
11.3 修改数组
import Cocoa
// 1. 数组末尾添加元素:append()方法或+=
var someInts: [Int] = []
someInts.append(10)
someInts += [20]
print(someInts[0])
print(someInts[1])
// 2. 通过索引修改数组元素值
someInts[1] = 30
print(someInts[1])
11.4 遍历数组
import Cocoa
// 使用for-in循环遍历数组
var someStrs: [String] = ["Hello", "World"]
for item in someStrs {
print(item)
}
11.5 合并数组
// 可以使用+合并两种已存在的相同类型数组
import Cocoa
var intA = [Int](repeating: 2, count: 2)
var intB = [Int](repeating: 1, count: 3)
var intC = intA + intB
for item in intC {
print(item)
}
11.6 属性
import Cocoa
// 1. count属性:统计数组元素个数
var intA = [Int](repeating: 2, count: 2)
var intB = [Int](repeating: 1, count: 3)
var intC = intA + intB
print(intC.count)
// 2. isEmpty属性:判断数组是否为空,如果为空返回true,不为空返回false
var varA: [Int] = []
var varB: [Int] = [1, 2, 3]
print(varA.isEmpty)
print(varB.isEmpty)
12 字典
- 字典用来存储无序的相同类型数据的集合。
- 字典中每个值(value)都有关联唯一的键(key),键是值的标识符,通过键访问值,键的类型没有限制但必须唯一
- 同数组一样,如果创建的字典赋值给变量则字典可以修改,赋值给常量则不可修改
12.1 创建字典
import Cocoa
// 1. 创建一个空字典,键的类型为Int,值的类型为String
// 法一(推荐):
var someDict1: [Int: String] = [:]
// 法二:
var someDict2 = [Int: String]()
// 2. 创建字典实例:注意冒号前无空格,冒号后有空格
var someDict3: [Int: String] = [1: "one", 2: "two"]
12.2 访问字典
import Cocoa
// 根据字典的键来访问值
var someDict: [Int: String] = [1: "one", 2: "two"]
var varA: String? = someDict[1] // 注意如果标注类型,必须是可选类型
var varB = someDict[2] // varB也是可选类型
print(varA!)
print(varB!)
12.3 修改字典
// 使用updateValue(, forKey: )增加或更新字典内容,如果key不存在,则添加该键值,如果key存在则修改对应的value,该方法返回原来旧的value的Optional类型值
import Cocoa
var someDict: [Int: String] = [1: "One", 2: "Two"]
var oldVal = someDict.updateValue("One 新的值", forKey: 1)
var newVal = someDict[1]
print(oldVal) // oldVal值为Optional("One")
print(newVal) // newVal值为Optional("One 新的值")
12.4 移除Key-Value对
// 法一:使用removeValue(forKey: ),返回Value的Optional类型值,如果不存在返回nil
import Cocoa
var someDict: [Int: String] = [1: "One", 2: "Tow", 3: "Three"]
var removeValue1 = someDict.removeValue(forKey: 2)
var removeValue2 = someDict.removeValue(forKey: 4)
print(removeValue1) // removeValue1值为Optional("Tow")
print(removeValue2) // removeValue2值为nil
// 法二:也可以通过指定键的值为nil来移除键值对
someDict[1] = nil
print(someDict[1]) // nil
12.5 遍历字典
import Cocoa
var someDict: [Int: String] = [1: "One", 2: "Tow", 3: "Three"]
// 法一:使用for-in循环遍历
for (key, value) in someDict {
print("键\(key)对应的值为\(value)")
}
// 键2对应的值为Tow
// 键1对应的值为One
// 键3对应的值为Three
// 法二:使用enumerated()遍历,返回的是字典的索引以及键值对
for (key, value) in someDict.enumerated() { // key是索引,value是键值对
print("索引\(key)对应的键值对为\(value)")
}
// 索引0对应的键值对为(key: 2, value: "Tow")
// 索引1对应的键值对为(key: 1, value: "One")
// 索引2对应的键值对为(key: 3, value: "Three")
12.6 字典转换成数组
// 使用字典的keys属性和values属性将键和值分别转变为数组
import Cocoa
var someDict: [Int: String] = [1: "One", 2: "Tow", 3: "Three"]
let dictKeys = [Int](someDict.keys)
let dictValues = [String](someDict.values)
// 以下是错误写法!!!
// let dictKeys: [Int] = someDict.keys
// let dictValues: [String] = someDict.values
for key in dictKeys {
print(key)
}
// 2
// 3
// 1
for value in dictValues {
print(value)
}
// Tow
// Three
// One
12.7 常用属性
import Cocoa
var someDict1: [Int: String] = [1: "One", 2: "Tow", 3: "Three"]
// 属性一:count属性统计键值对的个数
print(someDict1.count) // 3
// 属性二:isEmpty判断字典是否为空,空为true,不空为false
var someDict2: [Int: String] = [:]
print(someDict2.isEmpty) // true
13 函数
13.1 函数定义
- 使用func关键字定义
- 可指定0个、一个或多个输入参数和一个返回值类型
- 函数的实参传递顺序必须与形参列表相同,->后为函数的返回值类型
// 形式
// func funcname(形参名: 形参类型) -> returnType {
// ...
// return ...
// }
import Cocoa
func cityHello(city: String, hello: String) -> String {
return city + " " + hello
}
print(cityHello(city: "北京", hello: "你好!")) // 注意city和hello不能省略
13.2 函数参数
- 局部参数名:只能在函数体内部使用
import Cocoa
// city和hello是局部参数
func cityHello(city: String, hello: String) -> String {
return city + " " + hello
}
print(cityHello(city: "北京", hello: "你好!"))
- 外部参数名:在局部参数名前加外部参数名,再函数被调用时,必须使用外部参数名
import Cocoa
func printFunc(agrv hello: String) {
print("北京 \(hello)")
}
printFunc(argv: "你好")
// 忽略外部参数名
import Cocoa
func printFunc(_ hello: String) {
print("北京 \(hello)")
}
printFunc("你好")
- 可变参数:可以接受另个或多个参数,通过在变量类型后加…定义
import Cocoa
func vari<Int>(members: N...){
for i in members { print(i) }
}
vari(members: 3, 4, 5)
vari(members: 3.1, 4.1, 5.1)
vari(members: "你好", "北京")
13.3 值传递与引用传递
- 函数中的参数默认都是常量,只能查看,不能修改。
- 如果想要声明变量参数,进行引用传递,需要在参数类型前加inout关键字。且在调用时加&
import Cocoa
func swapFunc(_ a: inout Int, _ b: inout Int) {
var temp = a
a = b
b = temp
print("参数1在函数中的值为\(a),参数2在函数中的值为\(b)") // 2, 1
}
var x = 1
var y = 2
swapFunc(&x, &y)
print("参数1在函数外的值为\(x),参数2在函数外的值为\(y)") // 2, 1
13.4 函数返回值
元组作为返回值
- 元组与数组类似,不同的是,元组中的元素可以是任意类型,使用圆括号
- 可以使用元组类型让多个值作为一个复合值从函数中返回
// 定义一个函数,实现找到Int数组的最小值和最大值
import Cocoa
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value // 和C/C+不同的是尽管只有一条语句,也不能不加大括号和if写在一行
}
if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
let array: [Int] = [1, 2, 3, 4]
let val = minMax(array: array)
print("array的最小值为\(val.min),array的最大值为\(val.max)")
- 如果不确定返回的元组一定不为nil,则可以返回一个可选元组类型,例如
(Int, Int)?
,注意与(Int?, Int?)
不同,(Int, Int)?
整个元组是可选的,不只是元组中的每个元素值。上面找最小值和最大值的函数,如果传入的array为空,则应该返回nil
import Cocoa
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty { return nil }
var currentMin = array[0]
var currentMax = array[0]
for value in array[1 ..< array.count] {
if value < currentMin { currentMin = value}
if value > currentMax { currentMax = value}
}
return (min: currentMin, max: currentMax)
}
let array: [Int] = [1, 2, 3, 4, 5]
if let val = minMax(array: array) {
print("array的最小值为\(val.min),array的最大值为\(val.max)")
}
没有返回值函数
- 只需要去掉
-> return type
import Cocoa
func printFunc(hello: String) {
print("北京 \(hello)")
}
printFunc(hello: "你好")
13.5 函数类型
- 函数类型与其他类型类型,可以定一个类型为函数的常量或变量
import Cocoa
func sum(a: Int, b: Int) -> Int {
return a + b
}
var add: (Int, Int) -> Int = sum // 定义一个名为add的函数变量,该变量的参数类型和返回值都为Int,并将这个变量指向sum函数
print(add(1, 2)) // 输出3,注意不要加a和b
print(sum(a: 1, b: 2)) // 输出3,注意要加a和b
13.6 函数嵌套
函数嵌套指的是函数内定义一个新的函数,外部函数可以调用函数内定义的函数
14 闭包
14.1 定义与语法
-
定义:闭包是自包含的功能代码块,可以在代码中使用或者用来作为参数传值,和匿名函数类似
-
形式:
- 全局函数:有名字但不能捕获任何值
- 嵌套函数:有名字也能捕获封闭函数内的值
- 闭包表达式:无名闭包,使用轻量语法,根据上下文环境捕获值
-
语法
{(parameters) -> return type in
statements
}
import Cocoa
let divide = {(val1: Int, val2: Int) -> Int in
return val1 / val2
}
print(divide(200, 20))
14.2 闭包表达式
- sorted方法:会根据用于排序的闭包函数对数组中的值进行排序,该方法返回一个与原数组长度大小相同且排序正确的新数组,原数组不会被更改。
- sorted方法需要传入两个参数
- 已知类型的数组
- 闭包函数:传入两个与数组元素类型相同的两个值,返回布尔值
全局函数
import Cocoa
let num: [Int] = [2, 3, 1, 4]
// 使用全局函数提供排序,闭包函数类型为(Int, Int) -> Bool
func backwards(val1: Int, val2: Int) -> Bool {
return val1 > val2 // 从大到小排列
}
var reversed = num.sorted(by: backwards)
print(reversed) // [4, 3, 2, 1]
参数名称缩写
import Cocoa
let num: [Int] = [2, 3, 1, 4]
// swift自动为内联函数提供了参数名称缩写功能,可以直接通过$0,$1顺序调用闭包参数
var reversed = num.sorted(by: { $0 > $1 })
print(reversed) // [4, 3, 2, 1]
运算符函数
import Cocoa
let num: [Int] = [2, 3, 1, 4]
// 使用运算符排序
var reversed = num.sorted(by: >)
print(reversed) // [4, 3, 2, 1]
尾随闭包
import Cocoa
let num: [Int] = [2, 3, 1, 4]
// sorted()后的{ $0 > $1 }为尾随闭包
var reversed = num.sorted() { $0 > $1 }
print(reversed) // [4, 3, 2, 1]
14.3 捕获值
- 闭包可以在其定义的上下文中捕获常量或变量
- swift最简单的闭包是嵌套函数,即定义在其他函数内的函数,嵌套函数可以捕获其外部函数所有的参数及定义的常量和变量
import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() ->Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30
- 上面例子尽管incrementByTen是一个常量,但这个常量指向的闭包仍可以增加其捕获的变量值,因为函数和闭包都是引用类型。
- incrementByTen指向闭包的引用是一个常量,并非闭包本身是常量。因此如果将闭包赋值给了两个不同的常量/变量,两个值都会指向同一个闭包
import Cocoa
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementor() ->Int {
runningTotal += amount
return runningTotal
}
return incrementor
}
let incrementByTen = makeIncrementor(forIncrement: 10)
print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30
//
let alsoIncrementByTen = incrementByTen
print(alsoIncrementByTen()) // 40
15 枚举
15.1 定义与语法
- 定义:一种数据类型,只包含自定义的特定数据,是一组有共同特性的数据集合,声明在类中,通过实例化类来访问值
- 语法:使用enum和case关键词来创建枚举
- 注意:swift的枚举成员在创建时不会被赋予整型值
- 枚举、结构体、类的标识符(即名称)一般采用大驼峰(即第一个单词的首字母大写)
import Cocoa
enum DaysofWeek {
case Sunday
case Monday
case Tuesday
case Wednesday
case Thursday
case Friday
case Saturday
}
var weekDay = DaysofWeek.Thursday
weekDay = .Friday
15.2 相关值与原始值
相关值:可以是不同类型
import Cocoa
enum Student {
case Name(String)
case Mark(Int, Int, Int)
}
var studMarks = Student.Mark(1, 2, 3)
switch studMarks {
case .Name(let studName):
print("学生的名字是\(studName)")
case .Mark(let mark1, let mark2, let mark3):
print("学生的成绩是\(mark1)、\(mark2)、\(mark3)")
}
// 输出:学生的成绩是1、2、3
原始值:可以是整型、符点型、字符或字符串值。每个原始值在枚举声明中唯一。在原始值为整数枚举时,不需要显示的为每个成员赋值,swift会自动赋值,隐式赋值依次递增1,如果第一个值没有被赋处置,将会被自动设置为0
import Cocoa
enum Month: Int {
case January = 1, February, March, April, May, June, July, August, September, October, November, December
}
let yearMonth = Month.May.rawValue
print("月份为:\(yearMonth)") // 月份为:5
16 结构体
16.1 语法
- 通过struct关键字定义,可以定义属性和方法。结构体允许我们创建一个单一文件,且系统会自动生成面向其他代码的外部接口
import Cocoa
struct MarkStruct {
var mark: Int
init(mark: Int) { self.mark = mark }
}
var aStruct = MarkStruct(mark: 99)
var bStruct = aStruct
bStruct.mark = 100
print(aStruct.mark) // 99
print(bStruct.mark) // 100
16.2 应用
应用场景
- 几何形状的大小:封装一个width属性和height属性,两者均为Double类型
- 一定范围内的路径:封装一个start属性和length属性,两者均为Int类型
- 三维坐标系内一点:封装x,y和z属性,三者均为Double类型
注意:结构体实例是通过值传递而不是引用
17 类
类可以定义属性和方法,和其他语言不同,swift不要求为自定义类去创建独立的接口和实现文件,只需要在一个单一文件中定义一个类,系统会自动生成面向其他代码的外部接口
import Cocoa
class MarkClass {
var mark: Int
init(mark: Int) { self.mark = mark }
}
let marks = MarkClass(mark : 100)
print("成绩为\(marks.mark)")
17.1 类和结构体对比
共同点:
- 定义属性用于存储值
- 定义方法用于提供功能
- 定义附属脚本用于访问值?
- 定义构造器用于生成初始化值
- 通过扩展以增加默认实现的功能?
- 符合协议以对某类提供标准功能?
与结构体相比,类还有如下的附加功能
- 继承:允许一个类继承另一个类的特征
- 类型转换:允许在运行时检查和解释一个类实例的类型?
- 解构器:允许一个类实例释放任何其所被分配的资源
- 引用计数:允许对一个类的多次引用
17.2 恒等运算符
类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例
import Cocoa
class SampleClass {
let myProperty: String
init(s: String) { myProperty = s }
}
let spClass1 = SampleClass(s: "Hello")
let spClass2 = SampleClass(s: "Hello")
if spClass1 === spClass2 { print("引用相同的类实例") }
if spClass1 !== spClass2 { print("引用不同的类实例") }
18 属性
属性可分为存储属性和计算属性
18.1 存储属性
存储属性是存储在特定类或结构体的实例里的一个常量或变量,可以在定义存储属性的时候指定默认值,也可以在构造过程中设置或修改存储属性的值
import Cocoa
struct Number {
var digits: Int
let pi = 3.14 // 定义存储属性的时候指定默认值
}
var n = Number(digits: 123) // 构造过程中设置存储属性的值
n.digits = 1234 // 不会报错,构造过程中修改存储属性的值
// n.pi = 3.141 // 会报错,因为pi是常量
print(n.digits) // 1234
print(n.pi) // 3.14
18.2 延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性,必须将延迟存储属性声明为变量(var关键词),因为常量属性在构造完成之前必须要有初始值。延迟存储属性常用于延时对象的创建、属性的值依赖于其他未知类。
import Cocoa
class Sample {
lazy var no = Number() // lazy定义延迟存储属性,且该属性为var
}
class Number {
var name = "world"
}
var firstSample = Sample()
print(firstSample.no.name)
18.3 计算属性
计算属性不直接存储值,而是提供一个get来获取值,一个可选的set来设置其他属性或变量的值
import Cocoa
class Sample {
var no1 = 0.0, no2 = 0.0
var length = 300.0, breadth = 150.0
var middle: (Double, Double) {
get { return (length / 2, breadth / 2) }
set(axis) {
no1 = axis.0 - (length / 2)
no2 = axis.1 - (breadth / 2)
}
}
}
var result = Sample()
print(result.middle)
result.middle = (0.0, 10.0) // (150.0, 75.0)
print(result.no1) // -150.0
print(result.no2) // -65.0
18.4 只读计算属性
只有get没有set的计算属性为只读计算属性
import Cocoa
class Film {
var head = ""
var duration = 0.0
var metaInfo: [String: String] {
return [
"head": self.head,
"duration": "\(self.duration)"
]
}
}
var move = Film()
move.head = "Swift 属性"
move.duration = 3.09
print(move.metaInfo["head"]!) // Swift 属性
print(move.metaInfo["duration"]!) // 3.09
18.5 属性观察器
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器。可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性添加属性观察器。不需要为无法重载的计算属性添加属性观察器,因为可以通过set直接监控和响应值的变化。(用willSet和didiSet实现属性观察器)
import Cocoa
class Sample {
var counter: Int = 0 {
// 先执行willSet,再执行didSet
willSet(newTotal) { print("计数器:\(newTotal)") }
didSet {
if counter > oldValue { print("新增数:\(counter - oldValue)") } // oldValue为counter的旧值
}
}
}
let newCounter = Sample()
newCounter.counter = 100
newCounter.counter = 800
// 结果为:
// 计数器:100
// 新增数:100
// 计数器:800
// 新增数:700
18.6 类型属性
和实例属性区分开,类型属性是类型定义的一部分写在类型最外层的花括号里,使用关键字static定义值类型的类型属性,class定义类类型的类型属性
import Cocoa
struct StudMarks {
static let markCount = 97
static var totalCount = 0
var InternalMarks: Int = 0 {
didSet {
if InternalMarks > StudMarks.markCount {
InternalMarks = StudMarks.markCount
}
if InternalMarks > StudMarks.totalCount {
StudMarks.totalCount = InternalMarks
}
}
}
}
var stud1Mark1 = StudMarks()
var stud1Mark2 = StudMarks()
stud1Mark1.InternalMarks = 98
print(StudMarks.totalCount) // 获取类型属性
stud1Mark2.InternalMarks = 87
print(StudMarks.totalCount) // 获取类型属性
19 方法
在oc中,类是唯一能定义方法的类型,但在swift中,可以在类/结构体/枚举上定义方法
19.1 实例方法
- 实例方法可以访问和修改实例属性,提供与实例目的相关的功能
- 实例方法能够隐式访问它所属类型的所有的其他实例属性和方法
- 实例方法只能被它所属的类型的某个特定实例调用,不能脱离实例而被调用
import Cocoa
class Counter {
var count = 0
func increment() { count += 1 }
func incrementBy(amount: Int) { count += amount }
func reset() { count = 0 }
}
let counter = Counter()
counter.increment()
print(counter.count) // 1
counter.incrementBy(amount: 5)
print(counter.count) // 6
counter.reset()
print(counter.count) // 0
19.2 方法的局部参数名称和外部参数名称
swift默认给方法的第一个参数名称一个局部参数名称,默认给第二个和后续的参数名称为外部参数名称
import Cocoa
class division {
var count: Int = 0
func incrementBy(no1: Int, no2: Int) { // no1为局部参数名称,no2为外部参数名称
count = no1 / no2
print(count)
}
}
let counter = division()
counter.incrementBy(no1: 1800, no2: 3) // 600
19.3 self属性
每个实例都有self属性,self完全等同于实例本身
import Cocoa
class Cal {
let a: Int
let b: Int
let res: Int
init(a: Int, b: Int) {
self.a = a
self.b = b
self.res = a + b
}
}
let sum = Cal(a: 1, b: 2)
print(sum.res) // 3