首页 > 代码库 > Swift学习笔记(11)--类与结构体

Swift学习笔记(11)--类与结构体

类与结构是编程人员在代码中会经常用到的代码块。在类与结构中可以像定义常量,变量和函数一样,定义相关的属性和方法以此来实现各种功能。

和其它的编程语言不太相同的是,Swift不需要单独创建接口或者实现文件来使用类或者结构。Swift中的类或者结构可以在单文件中直接定义,一旦定义完成后,就能够被直接其它代码使用。

注意:一个类的实例一般被视作一个对象,但是在Swift中,类与结构更像是一个函数方法,在后续的章节中更多地是讲述类和结构的功能性。

1、类和结构的异同

类和结构有一些相似的地方,它们都可以:

定义一些可以赋值的属性;

定义具有功能性的方法

定义下标,使用下标语法

定义初始化方法来设置初始状态

在原实现方法上的可扩展性

根据协议提供某一特定类别的基本功能

更多内容可以阅读:属性,方法,下标,初始化,扩展和协议等章节

类还有一些结构不具备的特性:

类的继承性

对类实例实时的类型转换

析构一个类的实例使之释放空间

引用计数,一个类实例可以有多个引用

更多内容可以阅读:继承,类型转换,初始化自动引用计数

注意:结构每次在代码中传递时都是复制了一整个,所以不要使用引用计数

 

定义语法

类和结构拥有相似的定义语法,使用class关键词定义一个类,struct关键词定义结构。每个定义都由一对大括号包含:

class SomeClass {     // class definition goes here } struct SomeStructure {     // structure definition goes here }

注意:在定义类和结构时,一般使用UpperCamelCase命名法来定义类和结构的名称,比如SomeClass和SomeStructure,这样也符合Swift其它类型的标准。而给属性和方法命名时,一般时候lowerCamelCase命名法,比如frameRate和incrementCount等。
下面是一个结构和一个类的定义示例:

struct Resolution {     var width = 0     var height = 0 } class VideoMode {     var resolution = Resolution()     var interlaced = false     var frameRate = 0.0     var name: String? }

上面的例子首先定义了一个叫Resolution的结构,用来描述一个像素显示的分辨率,它有两个属性分别叫width和height。这两个属性被默认定义为Int类型,初始化为0.

之后定义了一个叫VideoMode的类,为视频显示的显示方式。这个类有四个属性,第一个属性resolution本身又是一个结构,然后是另外两个属性。最后一个属性用到了可选字符串类型String?,表示这个属性可以存在,或者不存在为nil。

类和结构的实例

上面的两个定义仅仅是定义了结构Resolution和类VideoMode的整体样式,它们本身不是一个特定的分辨率或者显示方式,这时候就需要实例化这个结构和类。

实例化的语法相似:

let someResolution = Resolution() let someVideoMode = VideoMode()

类和结构都使用实例语法来完成实例化。最简单的实例语法就是用两个括号()完成。在这种情况下定义的实例中的属性都会完成默认初始化。更多内容可以参考初始化一章。

访问属性

使用.语法就可以方便地访问一个实例的属性。在.语法中,在实例名之后加上(.)再加上属性名即可,不需要空格:

println("The width of someResolution is \(someResolution.width)") // prints "The width of someResolution is 0"

在这个例子中,someResolution.width表示someResolution的width属性,返回了它的初始值0

也可以使用.语法连续地获取属性的属性,比如VideoMode中resolution属性的width属性

println("The width of someVideoMode is \(someVideoMode.resolution.width)") // prints "The width of someVideoMode is 0"

使用这种方法不仅可以访问,也可以赋值:

someVideoMode.resolution.width = 1280 println("The width of someVideoMode is now \(someVideoMode.resolution.width)") // prints "The width of someVideoMode is now 1280"

注意:和Objective-C不同,Swift能够直接设置一个结构属性的子属性,就像上面这个例子一样。

结构类型的成员初始化方法

每个结构都有一个成员初始化方法,可以在初始化的时候通过使用属性名称来指定每一个属性的初始值:

let vga = Resolution(width: 640, height: 480)

但是和结构不同,类实例不能够使用成员初始化方法,在初始化一章有专门的介绍。

 

2、结构和枚举类型是数值类型

数值类型是说当它被赋值给一个常量或者变量,或者作为参数传递给函数时,是完整地复制了一个新的数值,而不是仅仅改变了引用对象。

事实上读到这里你已经在前面几章见过数值类型了,所有Swift中的基础类型-整型,浮点型,布尔类型,字符串,数组和字典都是数值类型。它们也都是由结构来实现的。

在Swift中所有的结构和枚举类型都是数值类型。这意味这你实例化的每个结构和枚举,其包含的所有属性,都会在代码中传递的时候被完整复制。

下面的这个例子可以说明这个特性:

let hd = Resolution(width: 1920, height: 1080) var cinema = hd

声明了一个常量hd,是Resolution的实例化,宽度是1920,高度是1080,然后声明了一个变量cinema,和hd相同。这个时候表明,cinema和hd是两个实例,虽然他们的宽度都是1920,高度都是1080。

如果把cinema的宽度更改为2048,hd的宽度不会变化,依然是1920

cinema.width = 2048  println("cinema is now \(cinema.width) pixels wide") // prints "cinema is now 2048 pixels wide"  println("hd is still \(hd.width) pixels wide") // prints "hd is still 1920 pixels wide"

这表明当hd被赋值给cinema时,是完整地复制了一个全新的Resolution结构给cinema,所以当cinema的属性被修改时,hd的属性不会变化。

下面的例子演示的是枚举类型:

enum CompassPoint {     case North, South, East, West } var currentDirection = CompassPoint.Westlet rememberedDirection = currentDirectioncurrentDirection = .East if rememberedDirection == .West {     println("The remembered direction is still .West") } // prints "The remembered direction is still .West"

尽管经过几次赋值,rememberedDirection依然没有变化,这是因为在每一次赋值过程中,都是将数值类型完整地复制了过来。

 

3、类是引用类型

和数值类型不同引用类型不会复制整个实例,当它被赋值给另外一个常量或者变量的时候,而是会建立一个和已有的实例相关的引用来表示它。

下面是引用的示例,VideoMode被定义为一个类:

let tenEighty = VideoMode() tenEighty.resolution = hdtenEighty.interlaced = true tenEighty.name = "1080i" tenEighty.frameRate = 25.0

分别将这个实例tenEighty的四个属性初始化,然后tenEighty被赋值给了另外一个叫alsoTenEighty的常量,然后alsoTenEighty的frameRate被修改了

let alsoTenEighty = tenEightyalsoTenEighty.frameRate = 30.0

由于类是一个引用类型,所以tenEighty和alsoTenEighty实际上是同一个实例,仅仅只是使用了不同的名称而已,我们通过检查frameRate可以证明这个问题:

println("The frameRate property of tenEighty is now \(tenEighty.frameRate)") // prints "The frameRate property of tenEighty is now 30.0"

注意到tenEighty和alsoTenEighty是被定义为常量的,而不是变量。但是我们还是可以改变他们的属性值,这是因为它们本身实际上没有改变,它们并没有保存这个VideoMode的实例,仅仅只是引用了一个VideoMode实例,而我们修改的也是它们引用的实例中的属性。

特征操作

因为类是引用类型,那么就可能存在多个常量或者变量只想同一个类的实例(这对于数值类型的结构和枚举是不成立的)。

可以通过如下两个操作来判断两个常量或者变量是否引用的是同一个类的实例:

相同的实例(===)

不同的实例(!==)

使用这些操作可以检查:

if tenEighty === alsoTenEighty {     println("tenEighty and alsoTenEighty refer to the same Resolution instance.") } // prints "tenEighty and alsoTenEighty refer to the same Resolution instance."

注意是相同的实例判断使用三个连续的等号,这和相等(两个等号)是不同的

实例相同表示的是两个变量或者常量所引用的是同一个类的实例

相等是指两个实例在数值上的相等,或者相同。

当你定义一个类的时候,就需要说明什么样的时候是两个类相等,什么时候是两个类不相等。更多内容可以从相等操作一章中获得。

指针

如果你有C,C++或者Objective-C的编程经验,你一定知道在这些语言中使用指针来引用一个内存地址。Swift中引用一个实例的常量或变量跟C中的指针类似,但是不是一个直接指向内存地址的指针,也不需要使用*记号表示你正在定义一个引用。Swift中引用和其它变量,常量的定义方法相同。

4、如何选择使用类还是结构

在代码中可以选择类或者结构来实现你所需要的代码块,完成相应的功能。但是结构实例传递的是值,而类实例传递的是引用。那么对于不同的任务,应该考虑到数据结构和功能的需求不同,从而选择不同的实例。

一般来说,下面的一个或多个条件满足时,应当选择创建一个结构:

结构主要是用来封装一些简单的数据值

当赋值或者传递的时候更希望这些封装的数据被赋值,而不是被引用过去

所有被结构存储的属性本身也是数值类型

结构不需要被另外一个类型继承或者完成其它行为

一些比较好的使用结构的例子:

一个几何形状的尺寸,可能包括宽度,高度或者其它属性,每个属性都是Double类型的

一个序列的对应关系,可能包括开始start和长度length属性,每个属性都是Int类型的

3D坐标系中的一个点,包括x,y和z坐标,都是Double类型

在其它情况下,类会是更好的选择。也就是说一般情况下,自定义的一些数据结构一般都会被定义为类。