Swift学习笔记(二)
类
值类型
在 Swift 中,所有的结构体和枚举都是值类型。这意味着它们的实例,以及实例中所包含的任何值类型属性,在代码中传递的时候都会被复制。
请看下面这个示例,Resolution是一个结构体:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
值类型的含义是:cinema.width和hd.width是互不影响的两个值。
引用类型
类是引用类型,与值类型不同,引用类型在被赋予到一个变量、常量或者被传递到一个函数时,操作的是引用,其并不是拷贝。因此,引用的是已存在的实例本身而不是其拷贝。
下面例子里VideoMode是类:
let tenEighty = VideoMode()
let alsoTenEighty = tenEighty
因为类是引用类型,所以tenEight和alsoTenEight实际上引用的是相同的VideoMode实例。换句话说,它们是同一个实例的两种叫法。
注意:Swift 中数组(Array)和字典(Dictionary)类型均以结构体的形式实现。 而Objective-C的NSArray和NSDictionary是按类的形式实现。
恒等
===
和!==
用来检测两个常量或者变量是否引用同一个实例。
属性
由于结构体(struct)属于值类型。当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。
属于引用类型的类(class)则不一样,把一个引用类型的实例赋给一个常量后,仍然可以修改实例的变量属性。
延迟属性
使用了延迟存储属性来避免复杂类的不必要的初始化,当第一次被调用的时候才会计算其初始值:
DataManager的一个功能是从文件导入数据,该功能由DataImporter类提供,DataImporter需要消耗不少时间完成初始化:因为它的实例在初始化时可能要打开文件,还要读取文件内容到内存。
DataManager也可能不从文件中导入数据。所以当DataManager的实例被创建时,没必要创建一个DataImporter的实例,更明智的是当用到DataImporter的时候才去创建它。
由于使用了lazy,importer属性只有在第一次被访问的时候才被创建。比如访问它的属性fileName时:
println(manager.importer.fileName)
// DataImporter 实例的 importer 属性现在被创建了
// 输出 "data.txt”
计算属性
类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
这个例子定义了 3 个几何形状的结构体:
- Point封装了一个(x, y)的坐标
- Size封装了一个width和height
- Rect表示一个有原点和尺寸的矩形
Rect也提供了一个名为center的计算属性。一个矩形的中心点可以从原点和尺寸来算出,所以不需要将它以显式声明的Point来保存。Rect的计算属性center提供了自定义的 getter 和 setter 来获取和设置矩形的中心点,就像它有一个存储属性一样。(这就是使用计算属性的原因。)
例子中接下来创建了一个名为square的Rect实例,初始值原点是(0, 0),宽度高度都是10。
square的center属性可以通过点运算符(square.center)来访问,这会调用 getter 来获取属性的值。跟直接返回已经存在的值不同,getter 实际上通过计算然后返回一个新的Point来表示square的中心点。如代码所示,它正确返回了中心点(5, 5)。
center属性之后被设置了一个新的值(15, 15),表示向右上方移动正方形。设置属性center的值会调用 setter 来修改属性origin的x和y的值,从而实现移动正方形到新的位置。
属性观察器
willSet观察器会将新的属性值作为固定参数传入,didSet观察器会将旧的属性值作为参数传入。
willSet和didSet观察器在属性初始化过程中不会被调用,它们只会当属性的值在初始化之外的地方被设置时被调用。
全局变量
全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量。
全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记lazy特性。 局部范围的常量或变量不会延迟计算。
类型属性
在 C 或 Objective-C 中,静态常量和静态变量的定义是通过特定类型加上global关键字。在Java中是static。
swift中,使用关键字static
来定义值类型比如struct、enum的类型属性,关键字class
来为类(class)定义类型属性。
方法
注意:函数指的是普通的func,方法指的是类中的成员方法。
函数参数可以同时有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用)。方法参数也一样(因为方法就是函数,只是这个函数与某个类型相关联了)。
Swift 默认仅给方法的第一个参数名称一个局部参数名称;默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。
incrementBy方法有两个参数: amount和numberOfTimes。默认情况下,Swift 只把amount当作一个局部名称,但是把numberOfTimes即看作局部名称又看作外部名称。下面调用这个方法:
let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15
你不必为第一个参数值再定义一个外部变量名:因为从函数名incrementBy已经能很清楚地看出它的作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时明确它的作用。
变异方法
结构体和枚举是值类型。一般情况下,值类型的属性不能在它的实例方法中被修改。
但是,如果你确实需要在某个具体的方法中修改结构体或者枚举的属性,你可以选择变异(mutating)这个方法,然后方法就可以从方法内部改变它的属性;并且它做的任何改变在方法结束时还会保留在原始结构中。方法还可以给它隐含的self属性赋值一个全新的实例,这个新实例在方法结束后将替换原来的实例。
要使用变异方法, 将关键字mutating 放到方法的func关键字之前就可以了:
上面的Point结构体定义了一个变异方法(mutating method)moveByX,moveByX用来移动点。moveByX方法在被调用时修改了这个点,而不是返回一个新的点。方法定义时加上mutating关键字,这才让方法可以修改值类型的属性。
注意:不能在结构体类型常量上调用变异方法,因为常量的属性不能被改变,即使想改变的是常量的变量属性也不行.
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// this will report an error
继承
重写需要显示标注override,如果想防止被重写可以加上final关键字。