12.1 存储属性
存储属性可以存储数据,分为常量属性(用关键字let
定义)和变量属性(用关键字var
定义)。存储属性适用于类和结构体两种Swift面向对象类型。
12.1.1 存储属性概念
我们在前面的章节中曾用到过属性,例如11.4节的员工类(Employee
)和部门类(Department
)。它们的类图如图12-1所示,Employee
的部门属性dept
与Department
之间进行了关联。
图 12-1 类图
我们可以在定义存储属性时指定默认值,示例代码如下:
class Employee {
let no : Int = 0
var name : String = ""
var job : String?
var salary : Double = 0
var dept : Department?
}
struct Department {
let no : Int = 0
var name : String = ""
}
var emp = Employee()
emp.no = 100 //编译错误 ①
let dept = Department()
dept.name = 30 //编译错误 ②
let emp1 = Employee()
emp1.name = "Tony" ③
实例通过点(.
)运算符调用属性,代码第①行试图修改常量属性,程序会发生编译错误。第②行代码也会发生编译错误,因为实例dept
本身是常量,即便它的属性name
是变量属性,也不能修改。但是代码第③行emp1.name = "Tony"
却可以编译通过,emp1
实例也是常量,name
是变量属性。这是因为emp1
是类实例,是引用类型,dept
是结构体实例,是值类型。引用类型相当于指针,其常量也可以修改,但值类型的常量是不能修改的。
12.1.2 延迟存储属性
由于Employee
和Department
有关联关系,Employee
类中的dept
属性关联到了Department
结构体。这种关联关系体现为:一个员工必然隶属于一个部门,而一个部门有很多员工。一个员工实例对应于一个部门实例。
下面看以下代码实现:
class Employee {
var no : Int = 0
var name : String = ""
var job : String?
var salary : Double = 0
var dept : Department = Department() ①
}
struct Department {
let no : Int = 0
var name : String = ""
}
let emp = Employee() ②
在代码第②行创建Employee
实例的时候,也会同时在第①行实例化dept
(部门)属性。然而程序或许不关心他隶属于哪个部门,只关心他的no
(编号)和name
(姓名)。虽然不使用dept
实例,但是仍然会占用内存。在Java中,有一种数据持久化技术叫Hibernate1。为了应对这种情况,Hibernate有一种延时加载技术,Swift也采用了延迟加载技术。修改代码如下:
1Hibernate是一种Java语言下的对象关系映射解决方案。它是使用GNU宽通用公共许可证发行的自由、开源的软件。它为面向对象的领域模型到传统的关系型数据库的映射,提供了一个使用方便的框架。——引自于维基百科 http://zh.wikipedia.org/wiki/Hibernate
class Employee {
var no : Int = 0
var name : String = ""
var job : String?
var salary : Double = 0
lazy var dept : Department = Department() ①
}
struct Department {
let no : Int = 0
var name : String = ""
}
let emp = Employee() ②
我们在dept
属性前面添加了关键字lazy
声明,这样dept
属性就是延时加载。延时加载,顾名思义,就是dept
属性只有在第一次访问它的时候才加载,如果永远不访问,它就不会创建,这样就可以减少内存占用。
12.1.3 属性观察者
为了监听属性的变化,可以使用Swift提供的以下属性观察者。
willSet
在设置新的值之前调用。didSet
在新值被设置之后马上调用。
我们可以根据需要,使用部分或全部的属性观察者。它们不能应用于延迟存储属性,但能够应用于一般的存储属性和计算属性。计算属性所需的属性观察者将在第15章介绍。