Swift 结构体
Swift 结构体是构建代码所用的一种通用且灵活的构造体。
我们可以为结构体定义属性(常量、变量)和添加方法,从而扩展结构体的功能。
与 C 和 Objective C 不同的是:
-
结构体不需要包含实现文件和接口。
-
结构体允许我们创建一个单一文件,且系统会自动生成面向其它代码的外部接口。
结构体总是通过被复制的方式在代码中传递,因此它的值是不可修改的。
struct Student{
//加入static关键字 只能通过Student直接获取
static let name = "Mike"
var height = 188
var score = 88
private static let habitDescript = "\(name)喜欢"
//只能struct 内部使用
public var habit = "\(Student.habitDescript)排球"
}
let stu = Student()
print(Student.name, stu.height, stu.score, stu.habit)
运行结果:Mike 188 88 Mike喜欢排球
克隆结构体
struct Mark{
var mark: Int
init(mark: Int) {
self.mark = mark
}
}
let mark1 = Mark(mark: 90)
var mark2 = mark1 //克隆一个
mark2.mark = 100;
print(mark1.mark, mark2.mark)
运行结果:90 100
结构体应用
在你的代码中,你可以使用结构体来定义你的自定义数据类型。
结构体实例总是通过值传递来定义你的自定义数据类型。
按照通用的准则,当符合一条或多条以下条件时,请考虑构建结构体:
- 结构体的主要目的是用来封装少量相关简单数据值。
- 有理由预计一个结构体实例在赋值或传递时,封装的数据将会被拷贝而不是被引用。
- 任何在结构体中储存的值类型属性,也将会被拷贝,而不是被引用。
- 结构体不需要去继承另一个已存在类型的属性或者行为。
举例来说,以下情境中适合使用结构体:
几何形状的大小,封装一个width属性和height属性,两者均为Double类型。
一定范围内的路径,封装一个start属性和length属性,两者均为Int类型。
三维坐标系内一点,封装x,y和z属性,三者均为Double类型。
struct Marks{
var math: Int
var english: Int
var chinese: Int
init(math: Int, english: Int, chinese: Int) {
self.math = math
self.english = english
self.chinese = chinese
}
}
let stuMark1 = Marks(math: 90, english: 92, chinese: 98)
let stuMark2 = Marks(math: 80, english: 87, chinese: 90)
print(stuMark1.math, stuMark1.english, stuMark1.chinese)
print(stuMark2.math, stuMark2.english, stuMark2.chinese)
运行结果:
90 92 98
80 87 90
Swift 类
Swift 类是构建代码所用的一种通用且灵活的构造体。
我们可以为类定义属性(常量、变量)和方法。
与其他编程语言所不同的是,Swift 并不要求你为自定义类去创建独立的接口和实现文件。你所要做的是在一个单一文件中定义一个类,系统会自动生成面向其它代码的外部接口。
类和结构体对比
Swift 中类和结构体有很多共同点。共同处在于:
- 定义属性用于存储值
- 定义方法用于提供功能
- 定义附属脚本用于访问值
- 定义构造器用于生成初始化值
- 通过扩展以增加默认实现的功能
- 符合协议以对某类提供标准功能
与结构体相比,类还有如下的附加功能:
- 继承:允许一个类继承另一个类的特征
- 类型转换:允许在运行时检查和解释一个类实例的类型
- 解构器:允许一个类实例释放任何其所被分配的资源
- 引用计数:允许对一个类的多次引用
恒等运算符
因为类是引用类型,有可能有多个常量和变量在后台同时引用某一个类实例。
为了能够判定两个常量或者变量是否引用同一个类实例,Swift 内建了两个恒等运算符:
恒等运算符 | 不恒等运算符 |
---|---|
运算符为:=== | 运算符为:!== |
如果两个常量或者变量引用同一个类实例则返回 true | 如果两个常量或者变量引用不同一个类实例则返回 true |
创建类:
class sampleClass: Equatable {
//遵守Equatable协议,必须实现此方法
static func == (lhs: sampleClass, rhs: sampleClass) -> Bool {
//判定两个常量或者变量是否引用同一个类实例
return lhs.myPropority == rhs.myPropority
}
var myPropority: String
init(string: String) {
self.myPropority = string
}
}
执行
let spClass1 = sampleClass(string: "123456")
let spClass2 = spClass1
spClass2.myPropority = "hello"
if spClass1 === spClass2 {// false
print("引用相同的类实例 \(spClass1)")
}
if spClass1 !== spClass2 {// true
print("引用不相同的类实例 \(spClass2)")
}
运行结果:
引用相同的类实例 project_konwledge_complement.sampleClass
Swift 属性
Swift 属性将值跟特定的类、结构或枚举关联。
属性可分为存储属性和计算属性:
存储属性 | 计算属性 |
---|---|
存储常量或变量作为实例的一部分 | 计算(而不是存储)一个值 |
用于类和结构体 | 用于类、结构体和枚举 |
存储属性和计算属性通常用于特定类型的实例。
属性也可以直接用于类型本身,这种属性称为类型属性。
另外,还可以定义属性观察器来监控属性值的变化,以此来触发一个自定义的操作。属性观察器可以添加到自己写的存储属性上,也可以添加到从父类继承的属性上。
存储属性
简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量。
存储属性可以是变量存储属性(用关键字var定义),也可以是常量存储属性(用关键字let定义)。
-
可以在定义存储属性的时候指定默认值
-
也可以在构造过程中设置或修改存储属性的值,甚至修改常量存储属性的值
struct Number
{
var digits: Int
let pi = 3.1415
}
var n = Number(digits: 12345)
n.digits = 67
print("\(n.digits)") //67
print("\(n.pi)") //3.1415
延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。
在属性声明前使用 lazy 来标示一个延迟存储属性。
注意:
必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
延迟存储属性一般用于:
-
延迟对象的创建。
-
当属性的值依赖于其他未知类
class Samber {
//lazy 后面必须跟var
lazy var des = Descript()
}
class Descript{
let descript = "hello world"
}
let sam = Samber()
print(sam.des.descript)
运行结果:hello world
计算属性
除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值
class CalculateP {
var x = 0.0, y = 0.0
var width = 100.0, height = 200.0
var middle: (Double, Double){
get{
return (width*0.5, height*0.5)
}
//如果计算属性的 setter 没有定义表示新值的参数名,则可以使用默认名称 newValue。
set(series){
x = series.0 - width * 0.5
y = series.1 - height * 0.5
}
}
}
let cal = CalculateP()
print(cal.middle)
cal.middle = (100.0, 100.0)
print(cal.x, cal.y)
结果:
(50.0, 100.0)
50.0 0.0
只读计算属性
只有 getter 没有 setter 的计算属性就是只读计算属性。
只读计算属性总是返回一个值,可以通过点(.)运算符访问,但不能设置新的值。
class readOnlyCalTrait {
var name: String = ""
var height: Int = 0
var Description: [String: String]{
return ["name": self.name,
"height": "\(self.height)"]
}
}
let classC = readOnlyCalTrait()
classC.name = "Jack"
classC.height = 180
print(classC.Description["name"]!, classC.Description["height"]!)
结果:Jack 180
注意:
必须使用var关键字定义计算属性,包括只读计算属性,因为它们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。
属性观察器
属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新的值和现在的值相同的时候也不例外。
可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重载属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器。
注意:
不需要为无法重载的计算属性添加属性观察器,因为可以通过 setter 直接监控和响应值的变化。
可以为属性添加如下的一个或全部观察器:
- willSet在设置新的值之前调用
- didSet在新的值被设置之后立即调用
- willSet和didSet观察器在属性初始化过程中不会被调用
class TraitObser {
var counter: Int = 0{
willSet{
print("newValue:\(newValue)")
}
didSet{
if self.counter > oldValue{
print("增加值:\(self.counter-oldValue)")
}
}
}
}
let obser = TraitObser()
obser.counter = 100
obser.counter = 500
运行结果:
newValue:100
增加值:100
newValue:500
增加值:400
全局变量和局部变量
计算属性和属性观察器所描述的模式也可以用于全局变量和局部变量。
局部变量 | 全局变量 |
---|---|
在函数、方法或闭包内部定义的变量。 | 函数、方法、闭包或任何类型之外定义的变量。 |
用于存储和检索值。 | 用于存储和检索值。 |
存储属性用于获取和设置值。 | 存储属性用于获取和设置值。 |
也用于计算属性。 | 也用于计算属性。 |
Swift 方法
Swift 方法是与某些特定类型相关联的函数
在 Objective-C 中,类是唯一能定义方法的类型。但在 Swift 中,你不仅能选择是否要定义一个类/结构体/枚举,还能灵活的在你创建的类型(类/结构体/枚举)上定义方法。
-
可以访问和修改实例属性
-
提供与实例目的相关的功能
实例方法要写在它所属的类型的前后大括号({})之间。
实例方法能够隐式访问它所属类型的所有的其他实例方法和属性。
实例方法只能被它所属的类的某个特定实例调用。
实例方法不能脱离于现存的实例而被调用。
在实例方法中修改值类型
Swift 语言中结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。
方法还可以给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
struct areaStr{
var length = 1
var breadth = 1
//mutating 关键词的函数才能修改值类型struct/enume 里面的值
mutating func scaleBy(res: Int){
length *= res
breadth *= res
print(length)
print(breadth)
}
}
var are = areaStr()
are.scaleBy(res: 3)
运行结果
3
3
类型方法
实例方法是被类型的某个实例调用的方法,你也可以定义类型本身调用的方法,这种方法就叫做类型方法。
声明结构体和枚举的类型方法,在方法的func关键字之前加上关键字static。类可能会用关键字class来允许子类重写父类的实现方法。
类型方法和实例方法一样用点号(.)语法调用。
class Math {
//类方法, 用类名称调用
class func absNumber(num: Int) -> Int {
if num > 0 {
return num
}else{
return -num
}
}
}
struct Math2 {
static func absNum(num: Int) -> Int{
if num > 0 {
return num
}else{
return -num
}
}
}
let abs1 = Math.absNumber(num: -90)
let abs2 = Math2.absNum(num: -80)
print(abs1, abs2)
运行结果:90 80