学习Swift笔记 (十)Swift的类和结构

来源:互联网 发布:查询电脑8000端口号 编辑:程序博客网 时间:2024/05/16 01:33

Swift的类和结构

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

注意:一个类的实例一般被视作一个对象,但是在Swift中,类与结构更像一个函数方法。


1. 类和结构的异同

相似:

· 定义一些可以赋值的属性。

· 定义具有功能性的方法。

· 定义下标,使用下标语法。

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

· 在原实现方法的可扩展性。

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


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

· 类的继承性

· 对类实例实时的类型转换

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

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


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


定义语法

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

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

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


类和结构的实例

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

实例化的语法相似:

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


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


访问属性

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

println("The width of someResolution is \(someResolution.width)")

也可以使用点语法连续的获取属性的属性:

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


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

someVideoMode.resolution.width = 1280

注意:和OC不同,Swift能够直接设置一个结构属性的子属性。


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

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

struct Resolution{    var width = 0    var height = 0}let vga = Resolution(width: 640, height: 480)


但是和结构不同,类实力不能够使用成员初始化方法。




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

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

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

struct Resolution{    var width = 0    var height = 0}let hd = Resolution(width: 1920, height: 1080)var cinema = hd
<span style="font-size:24px;font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="html">struct Resolution{    var width = 0    var height = 0}let hd = Resolution(width: 1920, height: 1080)var cinema = hdcinema.width = 2048println("cinema is now \(cinema.width) pixels wide")//cinema is now 2048 pixels wideprintln("cinema is now \(hd.width) pixels wide")//cinema is now 1920 pixels wide

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

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

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


3.  类是引用类型

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

struct Resolution{    var width = 0    var height = 0}class VideoMode{    var resolution = Resolution()    var interlaced = false    var frameRate = 0.0    var name: String?}let hd = Resolution(width: 1920, height: 1080)let tenEighty = VideoMode()tenEighty.resolution = hdtenEighty.interlaced = truetenEighty.name = "1080i"tenEighty.frameRate = 25.0let alsoTenEighty = tenEightyalsoTenEighty.frameRate = 30.0println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")//The frameRate property of tenEighty is now 30.0



特征操作

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

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

相同的实例(===)

不同的实例(!==)

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

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

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

当你定义一个类的时候,就需要说明什么样的时候是两个类相同,什么时候是两个类不相等。


指针

在C/C++/OC中,使用指针来引用一个内存地址。Swift中引用一个实例的常量或变量和C中的指针类似,但是不是一个直接指向内存地址的指针,也不需要使用*记号表示你正在定义一个引用。Swift中引用和其他变量,常量的定义方法相同。


4.  如果选择使用类还是结构

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


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

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

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

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

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


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

一个几何形状的尺寸,可能包括宽度,高度,或者其他属性。

一个序列的对应关系,可能包括开始start和长度length属性。

3D坐标系中的一个点,包括x,y,z坐标。


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


5. 集合类型的赋值和复制操作

Swift中,数组Array和字典Dictionary是用结构来实现的,但是数组与字典和其他结构在进行赋值或者作为参数传递给函数的时候有一些不同。

并且数组和字典的这些操作,又与Foundation中的NSArray和NSDictionary不同,它们是用类来实现的。


注意:下面介绍数组,字典,字符串等复制操作。这些复制操作看起来都已经发生,但是Swift只会在确实需要复制的时候才会完整复制,从而达到最优的性能。


字典的赋值和复制操作

每次将一个字典Dictionary类型赋值给一个常量或者变量,或者作为参数传递给函数时,字典会再赋值或者函数调用时,才会被复制。

如果字典中的键值是数值类型(结构或者枚举),它们在赋值的时候会同时被复制。相反,如果是引用类型(类或者函数),引用本身将会被复制,而不是类实力或者函数本身。字典的这种复制方式和结构相同。


var ages = ["Peter":23,"Wei":35,"Anish":65,"Katya":19]var copiedAges = agescopiedAges["Peter"] = 24println(ages["Peter"])//23


数组的赋值和复制操作

和字典Dictionary类型比起来,数组Array的赋值和复制操作就更加复杂。Array类型和C中的类似,仅仅只会在需要的时候才会完整的复制数组的值。

如果将一个数组赋值给一个常量或者变量,或者作为一个参数传递给函数,复制在赋值和函数调用的时候并不会发生。这两个数组将会共享一个元素序列,如果你修改了其中一个,另外一个也将会发生改变。

对于数组来说,复制只会在你进行了一个可能会修改数组长度操作时才会发生。包括拼接,添加或者移除元素等等。当复制实际发生的时候,才会像字典的赋值和复制操作一样。

var a = [1,2,3]var b = avar c = a//如果改变a中的某个值,会发现b和c中的数值也会跟着改变,因为赋值操作没有改变数组的长度:a[0] = 42println(a[0])//42println(b[0])//42println(c[0])//42//但是,如果在a中添加一个新的元素,那么就改变了数组的长度,这个时候就会发生实际的复制操作。如果在改变a中元素的值,b和c的元素将不会发生变化a.append(4)a[0] = 77println(a[0])//77println(b[0])//42println(c[0])//42

设置数组是唯一的

如果可以在对数组进行修改前,将它设置为唯一的就最好了。我们可以通过  使用unshare方法来讲数组自行拷贝出来,成为一个唯一的实体。

如果多个变量引用了同一个数组,可以使用unshare方法来完成一次“独立”。

b.unshare()

这时候如果在修改b的值,c的值也不会在受到影响。

var a = [1,2,3]var b = avar c = a//如果改变a中的某个值,会发现b和c中的数值也会跟着改变,因为赋值操作没有改变数组的长度:a[0] = 42println(a[0])//42println(b[0])//42println(c[0])//42//但是,如果在a中添加一个新的元素,那么就改变了数组的长度,这个时候就会发生实际的复制操作。如果在改变a中元素的值,b和c的元素将不会发生变化a.append(4)a[0] = 77println(a[0])//77println(b[0])//42println(c[0])//42b.unshare()b[0] = -105println(a[0])//77println(b[0])//-105println(c[0])//42

检查两个数组是否共用了相同的元素

使用实例相等操作符来判断两个数组是否共用了元素(===和!==)

var a = [1,2,3]var b = avar c = a//如果改变a中的某个值,会发现b和c中的数值也会跟着改变,因为赋值操作没有改变数组的长度:a[0] = 42println(a[0])//42println(b[0])//42println(c[0])//42//但是,如果在a中添加一个新的元素,那么就改变了数组的长度,这个时候就会发生实际的复制操作。如果在改变a中元素的值,b和c的元素将不会发生变化a.append(4)a[0] = 77println(a[0])//77println(b[0])//42println(c[0])//42b.unshare()b[0] = -105println(a[0])//77println(b[0])//-105println(c[0])//42if b === c {    println("b and c still share the same array elements.")}else{    println("b and c now refer to two independent sets of array elements.")}//b and c now refer to two independent sets of array elements.

也可以使用这个操作来判断两个子数组是否有共用的元素:

if b[0...1] === c[0...1]{    println("These two subarrays share the same element.")}else{    println("These two subarrays do not share the same elements.")}//These two subarrays do not share the same elements.

强制数组拷贝

通过调用数组的copy方法来完成强制拷贝。这个方法将会完整复制一个数组到新的数组中。

var names = ["Mohsen","Hilary","Justyn","Amy","Rich","Graham","Vic"]var copiedNames = names.copy()//通过改变copiedNames的值可以验证,数组已经被完整拷贝,不会影响到之前的数组:copiedNames[0] = "Mo"println(names[0])println(copiedNames[0])//Mohsen//Mo

注意:如果你不确定你需要的数组是否是独立的,那么仅仅使用unshare就可以了。而copy方法不管当前是不是独立的,都会完整拷贝一次,哪怕这个数组已经是unshare的了。







0 0