苹果Swift语言官方文档(中文翻译版)

本文档由长沙戴维营教育组织翻译和校对,由于英语水平有限,请大家指正。

长沙戴维营教育还为本教程录制了配套的视频教程在乌班图学院上免费提供,欢迎大家一起学习。

下面的章节,如果为蓝色链接表示以及翻译完毕,可以查看,如果为黑色则表示正在紧张的翻译中。本文档中文版每天都会更新,大家可以随时查看。如由Bug欢迎改正。


Swift编程

属性

属性是依赖于某个特定的类、结构体或者枚举类型的值。Swift有两种属性:存储类型和计算类型。其中存储类型可以作为实例的一部分存放变量或者常量的值,而计算类型的属性值是通过运算的来的。计算类型的属性可以在类、结构体和枚举类型中出现,但存储类型只可能出现在类和结构体类型中。

属性一般依赖于一个特定类型的实例,但是也可以依赖于类本身。依赖于类型本身的属性称为类型属性。

可以定义属性观察者来监督属性值的改变,从而作出响应。

存储属性

常量属性let的值在初始化后不能在改变,而变量属性var的值可以随时更改。

  1. struct FixedLengthRange {
  2. var firstValue: Int
  3. let length: Int
  4. }
  5. var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
  6. rangeOfThreeItems.firstValue = 6

结构体常量的存储属性

如果一个结构体实例被赋值给一个常量,则这个实例所拥有的存储类型的属性都不能在改变,包括变量属性在内。

  1. let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
  2. rangeOfFourItems.firstValue = 6
  3. //编译错误,firstValue的值不能改变

延时存储属性

延时存储属性的初始值直到第一次使用的时候才进行计算,在声明时通过@lazy进行标记。

提示 一定要将延时存储属性声明为变量(var),因为它的初始值可能会在实例初始化完成后才有,而常量属性一般在实例初始化完成之前就会有值。

当属性的初始值依赖于外部银子,并且该因子的值在实例初始化完成之前不确定时,延时属性非常有用。如果属性初始化时需要进行大量的计算,也可以考虑使用延时属性。

下面是延时属性的示例:

  1. class DataImporter {
  2. /*
  3. DataImporter是一个从外部文件导入数据的类。假设它需要花费较多的时间进行初始化
  4. */
  5. var fileName = "data.txt"
  6. //DataImporter类的数据导入功能
  7. }
  8. class DataManager {
  9. @lazy var importer = DataImporter()
  10. var data = String[]()
  11. //DataManager类提供数据管理功能
  12. }
  13. let manager = DataManager()
  14. manager.data += "Some data"
  15. manager.data += "Some more data"
  16. //DataImporter实例还没有创建

只有访问DataManagerimporter属性时才会去创建这个对象。

  1. println(manager.importer.filename)
  2. //创建importer属性并打印“data.txt"

存储属性与实例变量

Objective-C类的对象可以使用属性或者实例变量来存储值。Swift中并没有所谓的实例变量,而是将它们统一为属性了,这样使得属性的声明更加简化。

计算属性

除了存储属性外,类、结构体和枚举类型还可以定义计算属性。这些计算属性并不能够存储值,而是通过getter方法和可选的setter方法来间接的获取和设置其它属性和值。

  1. struct Point {
  2. var x = 0.0, y = 0.0
  3. }
  4. struct Size {
  5. var width = 0.0, height = 0.0
  6. }
  7. struct Rect {
  8. var origin = Point()
  9. var size = Size()
  10. var center: Point {
  11. get {
  12. let centerX = origin.x + (size.width / 2)
  13. let centerY = origin.y + (size.height / 2)
  14. return Point(x: centerX, y: centerY)
  15. }
  16. set(newCenter) {
  17. origin.x = newCenter.x - (size.width / 2)
  18. origin.y = newCenter.y - (size.height / 2)
  19. }
  20. }
  21. }
  22. var square = Rect(origin: Point(x: 0.0, y: 0.0),
  23. size: Size(width: 10.0, height: 10.0))
  24. let initialSquareCenter = square.center
  25. square.center = Point(x: 15.0, y: 15.0)
  26. println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
  27. //打印”square.origin is now at (10.0, 10.0)“

这个例子定义了三个结构体来表示几何形状:

  • Point封装了(x, y)坐标。
  • Size封装了宽度和高度。
  • Rect用坐标原点和大小定义一个矩形。

其中Rect结构体还提供了一个center的计算属性。这个属性的值是由矩形的originsize属性决定的,它本身并不需要存储信息。但是改变center的值,会间接的修改矩形的其它属性。 苹果Swift语言官方文档(中文翻译版) - 图1

简化setter的声明

如果没有为计算属性的setter的新值指定名字,则默认使用newValue。下面是Rect结构体的另外一种写法:

  1. struct AlternativeRect {
  2. var origin = Point()
  3. var size = Size()
  4. var center: Point {
  5. get {
  6. let centerX = origin.x + (size.width / 2)
  7. let centerY = origin.y + (size.height / 2)
  8. return Point(x: centerX, y: centerY)
  9. }
  10. set {
  11. origin.x = newValue.x - (size.width / 2)
  12. origin.y = newValue.y - (size.height / 2)
  13. }
  14. }
  15. }

只读的计算属性

如果一个计算属性只有getter而没有声明setter,则它是一个只读的计算属性。只读属性只能通过点语法返回一个值,而不能对它进行设置。

提示 必须使用var 声明计算属性,包括只读的计算属性在内,因为它们的值是可能改变的。而let只能用于常量的声明,表示它们的值不能发生改变。

还可以省略只读计算属性声明中的get关键字。

  1. struct Cuboid {
  2. var width = 0.0, height = 0.0, depth = 0.0
  3. var volume: Double {
  4. return width * height * depth
  5. }
  6. }
  7. let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
  8. println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
  9. //打印“the volume of fourByFiveByTwo is 40.0”

属性观察者

属性观察者用来观察和响应属性值的变化。每次设置属性的值都会调用相应的观察者,哪怕是设置相同的值。

可以给除延时存储属性以外的任何存储属性添加观察者。通过重写属性,可以在子类中给父类的属性(包括存储属性和计算属性)添加观察者。

提示 不需要给类本身定义的计算属性添加观察者,完全可以在计算属性的setter中完成对值的观察。

通过下面两个方法对属性进行观察:

  • willSet在属性的值发生改变之前调用。
  • didSet在设置完属性的值后调用。

如果没有给willSet指定参数的话,编译器默认提供一个newValue做为参数。同样,在didSet中如果没有提供参数的话,默认为oldValue

提示 willSetdidSet观察者在属性进行初始化的时候不会被调用。

  1. class StepCounter {
  2. var totalSteps: Int = 0 {
  3. willSet(newTotalSteps) {
  4. println("About to set totalSteps to \(newTotalSteps)")
  5. }
  6. didSet {
  7. if totalSteps > oldValue {
  8. println("Added \(totalSteps - oldValue) steps")
  9. }
  10. }
  11. }
  12. }
  13. let stepCounter = StepCounter()
  14. stepCounter.totalSteps = 200
  15. //About to set totalSteps to 200
  16. //Added 200 steps
  17. stepCounter.totalSteps = 360
  18. //About to set totalSteps to 360
  19. //Added 160 steps
  20. stepCounter.totalSteps = 896
  21. //About to set totalSteps to 896
  22. //Added 536 steps

提示 如果在didSet中给属性设置新值,最终结果就是最后设置的这个值。

全局变量与局部变量

上面关于计算属性和属性观察对全局变量和局部变量同样成立。全局变量定义在任意的方法、函数、闭包或者类型定义之外。而局部变量则定义在方法、函数或闭包之内。

之前遇到的全局变量或者局部变量都跟存储属性类型,都是用来存储值的。但实际上它们也能像计算属性有计算变量。

提示 全局变量和常量与延时属性类似,总是延时进行计算。但是它们并不需要使用@lazy标记。 局部常量和变量一定不是延时计算的。

类型属性

实例属性属于某个特定类型的实例。每次创建的实例,它都拥有自己的一组独立的属性值,不受其它实例对象影响。

你还可以定义属于类型本身的属性。这些属性是与具体的实例无关的,不管创建多少个实例都只有一份。这种属性称之为类型属性

可以给值类型(结构体和枚举类型)定义存储和计算类型的类属性,但是只能给类定义计算类型的类属性。值类型的存储属性可以是变量或常量。

提示 一定要给存储类型的类属性设置初始值。

类型属性语法

在C/Objective-C中只能使用全局静态变量来定义依赖与某个属性的变量或常量。但是在Swift中可以直接将它们定义为类型的一部分。其中结构体和枚举类型中使用static关键字,而在类类型中则使用class关键字。

  1. struct SomeStructure {
  2. static var storedTypeProperty = "Some value."
  3. static var computedTypeProperty: Int {
  4. //return an Int value here
  5. }
  6. }
  7. enum SomeEnumeration {
  8. static var storedTypeProperty = "Some value."
  9. static var computedTypeProperty: Int {
  10. //return an Int value here
  11. }
  12. }
  13. class SomeClass {
  14. class var computedTypeProperty: Int {
  15. //return an Int value here
  16. }
  17. }

提示 上面的计算属性都是只读的,但实际上可以定义为可读可写

使用类型属性

类型属性通过类型名字和点操作符进行访问和设置,而不是通过实例对象:

  1. println(SomeClass.computedTypeProperty)
  2. //print "42"
  3. println(SomeStructure.storedTypeProperty)
  4. //prints "Some value"
  5. SomeStructure.storedTypeProperty = "Another value."
  6. println(SomeStructure.storedTypeProperty)
  7. //prints "Another value."

下面演示了如何使用一个结构体来对声道音量进行建模,其中每个声道音量范围为0-10。 苹果Swift语言官方文档(中文翻译版) - 图2

  1. struct AudioChannel {
  2. static let thresholdLevel = 10
  3. static var maxInputLevelForAllChannel = 0
  4. var currentLevel: Int = 0 {
  5. didSet {
  6. if currentLevel > AudioChannel.thresholdLevel {
  7. currentLevel = AudioChannel.thresholdLevel
  8. }
  9. if currentLevel > AudioChannel.maxInputLevelForAllChannels{
  10. AudioChannel.maxInputLevelForAllChannels = currentLevel
  11. }
  12. }
  13. }
  14. var leftChannel = AudioChannel()
  15. var rightChannel = AudioChannel()
  16. leftChannel.currentLevel = 7
  17. println(leftChannel.currentLevel)
  18. //prints "7"
  19. println(AudioChannel.maxInputLevelForAllChannels)
  20. //prints "7"

当修改其中一个声道的值时,整个声道的音量最大值就可能发生改变。而每个声道都有自己的当前音量水平。