类和结构体—Swift学习笔记(十二)

注:本文为自己学习The Swift Programming Language的笔记,其中的例子为引用原书和其他博文或自己原创的。每个例子都会批注一些实践过程中的经验或思考总结。

1.基础

类和结构体是面对对象编程的核心,他们的概念并不陌生。

类和结构体可以相定义常量或变量和函数来定义属性和方法。与其他语言不同的事Swift不需要给出单独的接口和实现文件(Objective-C的复杂之处),Swift的类和结构体可以在单文件中定义,之后就可以直接使用,比较类似Python。

类的一个实例[instance]通常被称作一个对象[object],但由于Swift的类和结构体比起其他语言实用性更具有函数性,所以通称称之为一个实例。

2.类和结构体比较

Swift的类和结构体有许多相似之处,它们都可以:

(1)定义属性储存值

(2)定义具有功能性的方法

(3)定义下标并使用下标语法[点符号.]

(4)定义构造函数来给出初始值

(5)在原实现方法上扩展

(6)遵从协议来提供标准的功能

但类比结构体多出一下特性:

(1)类的继承

(2)类的实例可以实时类型转换

(3)类的析构函数可以释放实例分配的资源

(4)类使用引用计数,一个实例可以有多个引用(结构体是传值类型)

2.1类和结构体定义语法

类和结构体定义的方法类似,类型名最好使用UpperCamelCase规则,而参数和方法最好使用lowerCamelCase规则:

struct Resolution {
    var width = 0
    var height = 0
}

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

值得一说的是类和结构都有默认的不带参数的构造函数,比如Resolution(),因而根据类型推断resolution属性是一个Resolution结构体。

而String?作为可空类型,name被自动赋予nil初始值。

//有一点需要注意,类和结构体的存储属性在定义时要么直接带有初值,要么在构造函数中会出现赋值方式,总之它必须有一个初值的源头。

2.2类和结构体实例

我们用默认的无参数构造函数来创建类和结构的实例。

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

Swift还有一种默认的构造函数,它的参数是每个存储变量的初始值,在调用构造函数时,要给出构造函数的外部参数名(默认是属性名):

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

注:原书专门在后面安排初始化一章介绍初始化[Initialization]。

2.3访问属性

访问属性使用点符号,支持多级点符号访问。与Objective-C不同的是Swift支持多级点符号来设置子属性[subproperties]:

someVideoMode.resolution.width = 1280
println("The width of someVideoMode is \(someVideoMode.resolution.width)")
在例子中width就是someVideoMode的子属性,因为它是属性resolution的子属性。

2.4传值类型和传址类型

结构体和枚举类型都是传值类型,而类是传址类型。

传值类型直译是“值类型”[Value Types],传值类型在赋值时是直接创建一个拷贝,拷贝和源值只是在赋值语句执行时内容完全一样,而在之后其他代码执行时两个量没有任何关系。即改变一个量的值不会影响另一个量。

传址类型直译是“引用类型”[Reference Type],这里这么翻译是遵循了C语言的这个很形象的叫法,也和传值类型鲜明对比。传址类型在复制时是把源值的指针赋值给新值,用指针的思想来看就是两个指针指向内存的同一个位置。既然是指向同一个位置,不仅赋值语句执行时两个量内容完全一样,而以后其他代码执行时两个量仍然保持一致。即改变一个量的值另一个量的值做相同改变。

注:(1)传值类型和传址类型(或者说值类型和引用类型)的例子在前面的学习过程中已经有很多,关于结构体和枚举是传值类型、类是传址类型的详细例子参见本系列的[Swift学习笔记(二)]

(2)我们可以用指针的思想来理解传址类型/引用类型,但是要知道的是引用类型并不是直接指向内存的某个地址,它不需要*符号来定义,事实上引用类型和其他变量常量的定义是一样的。

2.5一致性运算符

Swift提供2种特殊的运算符来检查两个变量或常量是否指代类的同一个实例:

(1)===[一致]在两个变量或常量指代类的同一个实例返回true否则返回false,区别于==:[等于]是指两个实例在值上相等或等价,首先不一定是类其次一定要有可等性。

(2)!==[不一致]相反,与!=区别类似。

在定义类和结构体时,相等[equal to]和不想等[not equal to]的定义是由程序员显式给出的,在Advanced Operator一章中会详细的将这一点。

3.类和结构体的选择

类和结构体有很多相似之处,但由于传值调用和传址调用的不同它们也适用于不同场合。

结构体更加适用于一下情况:

(1)封装一些简单的类型的值

(2)希望封装的值在赋值是拷贝而不是引用

(3)结构体的所有属性都是传值类型

(4)不需要继承

一些结构体应用的很好的备选例子有:

(1)封装几何形状,宽度、高度等Double型特性

(2)封装一个系列的范围,开始、结束等Int型特性

(3)点坐标

其他情况特别要是用到继承考虑选择类。

4.集合类型的赋值和拷贝

Swift的数组和字典类型是作为结构体实现的,但是数组在赋值给常量或变量时或者传递给函数或方法时,它的拷贝行为和字典以及其他结构体有一点不同。

区别:Objective-C中NSArray和NSDictionary是类实现的,都采用引用调用。

拷贝[Copying]一词在代码中都是看起来像是要创建一个拷贝,而实际上Swift在后台只对必需拷贝的值进行拷贝,Swift会做好最大限度地优化。

4.1字典的赋值和拷贝

赋值一个字典实例或者把字典实例传递给函数或方法时,字典在这个时刻时被拷贝。如果健、值的类型是传值类型,那么它们在赋值或调用时也是被拷贝的。但如果它们之中有引用类型的,这时只是引用进行拷贝,而不是它们指代的类的实例或函数。

除数组以外其他的结构体的拷贝和字典相同。

var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19]
var copiedAges = ages

ages["Wei"] = 27
println(copiedAges["Wei"])
在修改了原来字典的值,拷贝字典并不改变值。

4.2数组的赋值和拷贝

数组的拷贝行为就要比字典稍微复杂一点了,它只在必要的时候拷贝。

当数组赋值和当作参数传入调用时,都是进行引用的拷贝,而不是实例的拷贝。

但是有一种情况,当一种行为有潜在的可能性修改数组长度时(包括附加[Append],插入[Insert],删除[Remove]或者用区间语法替换数组区间)数组进行值的拷贝。当这种拷贝发生时,数组的拷贝行为才和字典一样。

比如定义三个数组变量a,b,c,修改a[0]的值b和c的第一个元素同样改变,因为他们引用同一个实例:

var a = [1,2,3]
var b = a
var c = a

a[0] = 42
println("a[0] = \(a[0]), b[0] = \(b[0]), c[0] = \(c[0])")
//prints a[0] = 42, b[0] = 42, c[0] = 42
然而,在可能修改数组长度行为发生时,将新建拷贝:

a.append(77)
a[0] = 11
b[0] = 22
println("a[0] = \(a[0]), b[0] = \(b[0]), c[0] = \(c[0])")
//prints a[0] = 11, b[0] = 22, c[0] = 22
我们看到这里在a附加一个元素77后,为a进行了一次拷贝,而b、c仍然引用以前的实例。

4.3确保数组的独立性

当数组赋值或者传入时有时候有必要确保数组的独立性,数组变量可以调用unshare方法实现(常量不行,因为本来就不可变)。

如果同时几个数组变量引用一个实例,那么某一个变量在调用unshare方法时,Swift单独为它进行一次拷贝,其他不变。

b.unshare()
b[0] = 888
println("a[0] = \(a[0]), b[0] = \(b[0]), c[0] = \(c[0])")
//prints a[0] = 11, b[0] = 888, c[0] = 22
它的行为类似于4.2的第二个例子,这样一来三个原本引用同一实例的数组变量现在分别引用各自实例。

但是把调用过unshare方法的数组赋值给其他数组,这时仍然只是引用拷贝:

var e = b
e[0] = 456
println("a[0] = \(a[0]), b[0] = \(b[0]), c[0] = \(c[0]), e[0] = \(e[0])")
//prints a[0] = 11, b[0] = 456, c[0] = 22, e[0] = 456
b和e引用同一实例。

4.4检查数组引用的一致性

一致性算符可以用于检查数组是否引用同一个实例。

4.5强制拷贝数组

Swift提供一种在赋值时强制拷贝数组的方式,数组类型有一个copy方法,返回当前数组的一个拷贝,而不是引用:

var d = c.copy()
d[0] = 123
println("a[0] = \(a[0]), b[0] = \(b[0]), c[0] = \(c[0]), d[0] = \(d[0])")
//prints a[0] = 11, b[0] = 888, c[0] = 22, d[0] = 123

但是值得注意的一点是为了确保数组的独立性最好还是使用unshare方法,而非首选copy方法。因为unshare方法是在必要时才进行拷贝,而copy方法是一直拷贝的。











  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值