文章目录
  1. 1. 1、概要
  2. 2. 2、存储属性
    1. 2.1. 2.1、基础知识
    2. 2.2. 2.2、延迟存储属性(Lazy Stored Properties)
  3. 3. 3、计算属性
  4. 4. 4、属性观测器
  5. 5. 5、类型属性

1、概要

属性用来描述类,结构体或者枚举里面的值。属性分为存储属性和计算属性,存储属性存储类中常量和变量的值,而计算属性计算常量或者变量的值。存储属性只能放到类和结构体当中。计算属性可以放到类,结构体和枚举当中。
存储属性类似于Objective-C中的成员变量。
计算属性就是Objective-C中的gettersetter器。

2、存储属性

2.1、基础知识

在类和结构体中定义存储属性,枚举中不支持。可以使用var和let修饰存储属性,分别代表变量存储属性和常量存储属性(赋值后不可变更)。两种存储属性都可以在定义时候赋值,并且常量存储属性可以在类和结构体的构造器中赋值。使用The Swift Language Programming的例子,然后加了一些自己的理解和说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
struct FixedLengthRange {
var firstValue: Int //变量,随时可以修改
let length: Int //常量,初始化后不能修改
}
//使用逐一成员构造器对成员进行初始化
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
rangeOfThreeItems.firstValue = 6
//报错,不能对结构体常量进行赋值
rangeOfThreeItems.length = 4
//定义常量实例,结构体里面所有的存储属性变成常量,不能修改
let rangeOfFourItems = FixedLengthRange(firstValue: 1, length: 4)
//rangeOfFourItems.firstValue = 10 --错误,不能修改
class FixedLengthRangeClass
{
var firstValue: Int = 0
let length: Int = 1
}
let rangeClass = FixedLengthRangeClass()
rangeClass.firstValue = 2 --可以修改
rangeClass.length = 2 --不能修改,因为定义为常量
//原因是结构体是值类型的,定义一个常量结构体实例,
//对应的存储属性都变成了常量。而类是引用类型的,
//只是实例是常量,里面的存储属性跟本身定义一样。

2.2、延迟存储属性(Lazy Stored Properties)

关键字lazy加在存储属性前面,表示值将在第一次使用的时候,才做初始化。而且lazy关键字不能修饰常量存储属性(let修饰),因为常量存储属性必须在初始化完成之前赋值。lazy的应用场景一般用在需要初始化的对象需要比较复杂的操作的时候,比如下面的例子里面,去初始化一个文件对象的时候,需要进行文件初始化操作,这比较耗时。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class DataImporter {
//文件导入类,指定文件路径,获取文件内容,操作很耗时
var fileName = "data.txt"
init(){
//获取文件内容
}
}
class DataManager {
lazy var importer = DataImporter() //定义importer为延迟存储属性
var data = [String]()
//定义处理函数...
}
let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
//---
//以上代码,importer还没有用到,所以没有初始化
//下面这句代码,第一次使用importer,所以这时候开始创建importer属性。
println(manager.importer.fileName)

3、计算属性

类,结构体和枚举都可以定义计算属性,计算属性使用getter来获取值,使用setter来间接设置其他属性的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
struct Point
{
var x = 0.0, y = 0.0
}
struct Size
{
var width = 0.0, height = 0.0
}
//下面的结构体Rect定义了一个计算属性center,表示这个长方形类的中心点。
//注意计算属性不能定义为constant.也就是只能使用var来定义。
//每一个计算属性都必须有一个getter,但是不需要setter
//(不需要时,该计算属性就是只读的)。
//在定义setter时,可以使用自定义的参数名来表示新值,例如下面的newCenter。
//而newCenter的类型需要跟计算属性类型保持一致,
//下面的例子是Point类型。而显示提供一个类型会报错。
struct Rect
{
var origin = Point()
var size = Size()
var center: Point
{
get
{
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter)
{
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
// 通过调用默认的结构体逐一成员构造器生成一个实例
var square = Rect(origin: Point(x: 0.0, y: 0.0),
size: Size(width: 10.0, height: 10.0))
//获得计算属性center(getter),
//赋值给initialSquareCenter,访问方式跟其他属性一样。使用.号来获取。
let initialSquareCenter = square.center
//设置计算属性center(setter),
//设置后原点origin会从(0,0)变成(10,10)
square.center = Point(x: 15, y: 15)
square.origin
//在定义setter时,可以不使用自定义参数,使用Swift默认的setter值newValue来代替,
//所以计算属性定义可以简化为如下代码:
struct AlternativeRect
{
var origin = Point()
var size = Size()
var center: Point
{
get
{
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set
{
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
// 定义一个只读的计算属性
struct Cube
{
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double
{
get
{
return width * height * depth
}
}
}
//只读的计算属性,可以取掉get{},简化为以下形式
struct AnotherCube
{
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double
{
return width * height * depth
}
}
var cube = AnotherCube(width: 4, height: 5, depth: 2)
cube.volume = 8 //因为volume是只读的,所以此语句会报错

4、属性观测器

  • 属性观测器只作用在存储属性上
  • 属性观测器不能作用在延迟存储属性上(lazy)
  • 属性观测器不能作用在常量存储属性上(let)
  • willSet在设置新值之前调用,使用默认参数newValue访问新的属性值,也可以自定义参数
  • didSet在设置新值完成后调用,使用默认参数oldValue访问旧的属性值,也可以自定义参数
  • 在初始化过程中不会触发willSetdidSet
  • 属性观测器可以重载,这个知识点以后会提到

让我们看看具体的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class StepCounter
{
var totalSteps: Int = 0
{
//使用newTotalSteps作为新的值,在willSet中使用
willSet(newTotalSteps)
{
"About to step to \(newTotalSteps)"
}
//使用oldTotalSteps作为旧的值,在didSet中使用
didSet(oldTotalSteps)
{
"Just stepped from \(oldTotalSteps)"
}
}
}

还可以使用默认值进行调用,可以简化代码:

1
2
3
4
5
6
7
8
class StepCounterShorter
{
var totalSteps: Int = 0
{
willSet{ "About to step to \(newValue)" }
didSet { "Just stepped from \(oldValue)" }
}
}

另外,我们可以在willSetdidSet中对本属性进行赋值。会产生一下结果:

  • willSet中,对本属性进行赋值,会产生编译警告:”Attemping to store to property ‘xxx’ within its own willSet, which is about to be overwritten by the new value”, 意思就是设置了也没有用,因为会值会被赋值的新值所覆盖。

  • didSet中,因为已经设置了新值,如果在didSet中再次进行赋值,则会覆盖掉设置的新值,但是不会再次触发属性观测器。

看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
class StepCounterShorterWithModify
{
var totalSteps: Int = 0
{
willSet{ totalSteps = 0 } //触发编译警告
didSet { totalSteps = totalSteps + 20 }//会覆盖掉newValue
}
}
var stepper = StepCounterShorterWithModify()
stepper.totalSteps = 345
stepper.totalSteps // 输出365

5、类型属性

顾名思义,类型属性就是跟类型相关的属性,不跟实例相关。类似于C中的静态变量和静态常量。

  • 对于值类型(结构体和枚举),可以定义存储和计算类型属性,使用关键字static定义
  • 对于引用类型(类),可以定义计算类型属性,使用关键字class定义,这时计算类型属性可以被子类重载
  • 类也可以static定义存储属性和计算属性,表示类型属性
  • static var表示静态变量,static let表示静态常量

下面是代码+说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
struct SomeStructure
{
//定义一个存储类型属性变量,可以更改值
static var storedTypeProperty = "some value"
//定义一个存储类型属性常量,不可以更改值
static let constantStoredTypeProperty = "some value"
//定义一个只读的计算类型属性
static var computedTypeProperty: Int { return 4 }
}
SomeStructure.storedTypeProperty = "good"
SomeStructure. constantStoredTypeProperty = "good" //编译错误:can't assign to the result of this expression
//与结构体类似的枚举的例子
enum SomeEnum
{
static var storedTypeProperty = "some value"
static var computedTypeProperty: Int { return 4 }
}
class SomeClass
{
//static也可以修饰类的存储属性
static var storedTypeProperty = 10
//static 修饰类的计算属性,表示class+final
static var finalComputedTypeProperty : Int {return 20} // = "some value"
//class修饰类的技术属性,可以被子类重载
class var overrideComputedTypeProperty: Int { return 4 }
// class定义了计算类型属性,可以被子类重载
class var computedTypeProperty: Int
{
get
{
return 4
}
set
{
//内部也可以通过self对类型属性进行访问
self.storedTypeProperty = newValue
}
}
}
SomeClass.computedTypeProperty = 10
SomeClass.storedTypeProperty //结果为10
文章目录
  1. 1. 1、概要
  2. 2. 2、存储属性
    1. 2.1. 2.1、基础知识
    2. 2.2. 2.2、延迟存储属性(Lazy Stored Properties)
  3. 3. 3、计算属性
  4. 4. 4、属性观测器
  5. 5. 5、类型属性