结构体和类其实是一个用通用且灵活的方法来构造代码块的东西。而我们可以用定义常量,变量或函数的方法来定义结构体和类的属性方法,以此来扩展该结构体或类的功能。通常很官方的理解还不如翻译成自己的白话文容易理解很概念性的东西。
结构体和类的相同点:
相比结构体,类有下面这些额外的优势:
定义结构体(structure)和类(class)的方法其实是比较相似的。用struct
和class
关键字分别引出介绍该结构体和类。结构体和类的定义内容需要放在大括号内
struct SomeStructure {
// 这里是结构体的定义
// 结构体的名称(SomeStructure)要大些
}
class SomeClass {
// 这里是类的定义
}
不管什么时候定义结构体或者类的时候都要用大写结构体或类的名称,命名结构体或类时等于说创建了一个新的swift 类型,用大写的原因是因为我们要匹配swift 类型(String Bool …)的命名规则,而在定义属性或方法的时候要用小写。注意区分。。。
下面是一个定义结构体和类的实例,从该例子中可以隐约看到结构体和类的共同点和不同点
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
VideoMode类里面有一个变量属性的引用 var resolution = Resolution()
其实这种写法(大写字母后面跟个括号 一般情况下都是引用结构体,函数等)。该引用说明了 VideoMode类的一个属性它要继承来自另一个结构体里面的属性,所以类比结构体多一个特性就是 类它可以继承结构体或另一个类里面的特性。
上面案例里的分辨率(Resolution)结构体和视频模式(VideoMode)类的定义仅仅只是描述了什么是结构体和类或者该结构体或类由哪些东西组成。而并没有具体描述分辨率是多少,视频模式有哪几种。所以在这个时候我们就需要创建一个实例来具体描述分辨率和视频模式。
// 实例化结构体和类的写法比较相似
let someResolution = Resolution()
let someVideoMode = VideoMode()
利用初始化语法来创建一个新的实例用此来获取初始值。初始化语法的依法一般是在结构体或类的名字后面添加一个空白的小括号。就像Resolution()
和VideoMode()
可以用dot语法来读取实例的某一个属性,可以在实例名称后面加上想要读区的属性,在用.
开隔开实例名和属性名 中间没用空格。
print("The width of someResolution is \(someResolution.width)")
// 输出:The width of someResolution is 0
同样也可以用dot语法来分配一个新值给变量属性
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// 输出:The width of someVideoMode is 0
Swift的结构体支持成员逐一初始化器操作。也就是说可以用初始化结构体实例的成员,结构体初始值会由这个成员逐一初始化器会通过实例的名称将初始化的值传递到结构体的属性里面
let vga = Resolution(width: 640, height: 480)
上面是结构体的实例,成员逐一初始化会将640,480通过结构体的名称(Resolution)传递并初始化这两个值给结构体的属性。
swift语言里面什么是值类型呢?结构体和枚举的值类型其实是一个可以通过传递和赋值起所在的结构体或枚举里面的属性值 。
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
上面的例子中Resolution(width: 1920, height: 1080)
就是常量hd的一个值。变量cinema则引用了hd。也可以用dot语法来读取cinema的宽度(cinema.width
) 并且重新给其分配一个值(2048
)。
print("cinema is now \(cinema.width) pixels wide")
// 输出:cinema is now 2048 pixels wide
print("hd is still \(hd.width) pixels wide")
// 输出:hd is still 1920 pixels wide
重新分配后的cinema宽度将会改变为2048,而hd的宽度则不会变,是因为变量cinema是引用了hd的属性,从而使实例cinema也会继承hd所拥有的属性 也就是继承hd后cinema属性是width: 1920 height: 1080,不同的是继承后的hd和cinema它们的属性都是各自独有且相同的,改变cinema的属性并不会改变hd的属性。这个例子是对结构体和类的实例属性进行的操作。即结构体或类的属性可以进行引用或继承,引用或继承实例化后结构体或类的属性后,改变引用后的结构体或类的属性从而会复制或创建一个新的属性。不会对原结构体或类的属性做同步改变。原结构体或类的属性不变。
enum CompassPoint {
case north, south, east, west
// mutating: 变形,突变
mutating func turnNorth() {
self = .north
}
}
mutating
关键字用于修改结构体,类,枚举中的变量。
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
// 输出:The current direction is north
print("The remembered direction is \(rememberedDirection)")
// 输出:The remembered direction is west
现在的变量currentDirection它的值是枚举的一个成员west。创建一个新的常量叫rememberDirection它的值引用了currentDirection的值 也就是west,currentDirection.turnNorth() - 用dot语法读取可以读取结构体,类和枚举的属性,属性不一定是常量,变量也有可能是一个变形的函数。该函数turnNorth()
就有一个属性且该属性的值就是north。Line3重新分配值的时候currentDirect就会引用函数里面的属性。
结构体和枚举可以被用作值类型 而类确实引用类型,实例化后的类,它的变量或常量被床底给某个函数的时候,值是不会被复制的,下面这个例子可以举例说明。
// 实例化VideoMode类
let tenEighty = VideoMode()
// 属性
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
/* 类的实例再引用 会创建一个新的实例
和结构体枚举不同的是
类实例的再引用 创建新的实例会指代同一个类的属性
*/
let alsoTenEighty = tenEighty
// 重新分配frameRate一个值
alsoTenEighty.frameRate = 30.0
因为类是一个引用类型,有时候我们创建的类的实例会被多个变量或常量所引用。再去创建一个新的实例,而新的实例有引用同一个类的属性,类的引用就是引用类的属性。它不会像结构体或枚举那样,创建一个新的实例并复制一个新的属性。
结构体和枚举是值类型,再创建一个新的实例引用原结构体或枚举的实例时,也会创建一个新的实例同样也会复制原结构体或枚举的属性。恒等运算符或非恒等运算符是用来检查,两个常量或变量会不会引用同一个类的实例。
恒等运算符 ( === )
非恒等运算符 ( !== )
// 用if条件语句判断两个实例的属性是否相等
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}