我们都知道,类是由属性和方法组成的,一般而言,属性主要是用来访问数据成员,而方法则是用来执行某些操作,比如说计算数据等等。在Objective-C中,属性主要是用来访问封装之后的数据成员,属性本身并不存储数据,数据是由数据成员来存储。但是在Swift中,属性承担了多种角色,它远比Objective-C中的属性更为强大。
Swift中的属性可以分为存储属性和计算属性,其中存储属性就相当于Objective-C中的数据成员,主要是用来存储数据;而计算属性则可以通过计算其它属性的值来返回数据。下面我们来对它们做一个了解。
1、存储属性
Swift中的存储属性在形式上和普通的常量、变量并无不同。一般而言,属性在类被初始化的时候也要被初始化,否则就应该将其声明为可选类型。比如说:
class Student {
// 存储属性
var name: String? // 声明为可选类型
var age: Int = 0 // 初始化为默认值
var sex: String = "Female"
var height: Double = 0.0
}
let p = Student()
p.name = "LeBron James"
p.age = 33
p.sex = "Male"
p.height = 2.03
出于性能方面的考虑,有时候我们并不希望一个属性在被使用之前就被加载进内存,对于这种情况,我们可以使用关键字lazy来修饰:
class Student {
// 存储属性
var name: String?
// 延迟加载属性
lazy var dog: Dog = Dog()
}
struct Dog {
var name: String?
var age: Int = 0
}
// 实例化Student类
let p = Student()
p.name = "LeBron James"
// 用到dog属性时,它才会被加载
p.dog.name = "WangCai"
假如我们没有用关键字lazy对Student类的dog属性进行修饰,那么当我们创建p实例的时候,它也会被实例化并被加载进内存。而使用lazy进行修饰之后,只有当我们用到它时,它才会被加载。另外,一定要注意,存储属性只适用于类和结构体这两种类型,并不适用于枚举。
2、计算属性
计算属性本身并不会存储数据,而是从其它存储属性中计算得到数据。计算属性不仅可以用于类和结构体,还可以用于枚举。计算属性的格式为:
class(struct/enum) 类型名 {
存储属性
var 计算属性名: 属性数据类型 {
get {
return 计算完成后属性的值
}
set(新属性的值) {
// 其它代码
}
}
}
计算属性中提供一个get用来获取值,另外一个set用来设置其它属性或变量的值,其中set方法是可选的。另外,计算属性应该使用var来修饰。
3、只读计算属性
计算属性可以只有get访问器,而没有set访问器,像这种计算属性就是只读计算属性。只读计算属性还可以直接省略掉关键字get及其后面的花括号,直接返回计算后的值。只读计算属性在实际开发过程经常会用到,比如说设置导航控制器顶部状态栏的颜色:
// 重写父类状态栏管理的属性(这是一个只读的计算属性,可以省略get{},直接return就可以了)
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
4、属性监听器
为了监听属性的变化,Swift中提供了属性监听器。属性监听器能够及时的监听存储属性的变化,即便是存储属性变化前后的值相同,它也能够监听到。但是,一定要注意,属性监听器并不能监听延迟存储属性(也就是用lazy修饰的存储属性)和常量属性的变化。
属性监听器中有两个重要的关键字,分别是willSet和didSet。其中,willSet在属性监听器修改之前调用,而didSet在属性监听器修改之后调用。属性监听器的格式为:
class(struct) 类型名 {
其它存储属性
var 存储属性名: 存储属性的数据类型 = 初始化值 {
willSet(新值) {
// 代码
}
didSet(旧值) {
// 代码
}
}
}
注意,因为枚举不支持存储属性,所以属性监听器并不能在枚举中使用。下面,我们看一个具体的示例:
// 属性监听器
class Person {
var age: Int = 0
var name: String = "Enrica" {
willSet(newValue) {
print("变量name的新值:\(newValue)")
}
didSet(oldValue) {
print("变量name的旧值:\(oldValue)")
}
}
var height: Double = 0
}
var p = Person()
p.age = 10
// 属性监听器一旦监听到name值的改变,就会调用willSet和didSet方法
p.name = "LeBron"
在上面的Person类中,存储属性name有一个默认的值“Enrica”,当我们使用p.name = "LeBron"重新给它赋值时,属性监听器立马就能监听到,然后做出相应的反映。另外,其实willSet和didSet后面小括号中的参数是可以省略的,系统会给它们分配一个默认的参数。比如说,上面的代码可以修改为:
// 属性监听器
class Person {
var age: Int = 0
var name: String = "Enrica" {
willSet {
print("变量name的新值:\(newValue)")
}
didSet {
print("变量name的旧值:\(oldValue)")
}
}
var height: Double = 0
}
var p = Person()
p.age = 10
// 属性监听器一旦监听到name值的改变,就会调用willSet和didSet方法
p.name = "LeBron"
代码修改之后,运行的结果一毛一样。属性监听器看起来很麻烦,但是实际应用非常的广泛,所以一定要掌握。