本文原载于我的博客:https://www.seekingmini.top/archives/swift踩坑对属性的理解
写在前面
在参考了官方英文文档以后,对于属性这个概念有了一些自己的思考。
属性
官方没有给出一个明确的定义,只是告诉我们属性是干什么的:
Properties associate values with a particular class, structure, or enumeration.
属性将值和特定的类、结构体或者枚举关联。这话说了跟白说一样。我们先暂且不给属性下定义,先来看看属性的分类。属性有两种——存储属性和计算属性。
存储属性
In its simplest form, a stored property is a constant or variable that is stored as part of an instance of a particular class or structure.
提取关键句——存储属性是一个常量或者变量,这个变量或者常量是特定的类、结构体或者枚举的实例的一部分。这里我们至少知道了存储属性是一个常量或者变量。我们继续看:
Stored properties can be either variable stored properties (introduced by the
var
keyword) or constant stored properties (introduced by thelet
keyword).
存储属性分为变量存储属性和常量存储属性。来看个例子:
struct Person {
let name: String
var age: Int
}
var person = Person(name: "Bob", age: 22)
上面的例子中,name
是一个常量存储属性,而age
是一个变量存储属性。从字面上来看,存储属性是用来存值的,我们可以对这个属性读或写(取决于它是常量或者变量)。
延迟存储属性
A lazy stored property is a property whose initial value is not calculated until the first time it is used.
当一个存储属性加了修饰符lazy
时,只有当我们第一次使用这个属性时,它的值才会被计算。看个例子:
class DataImporter {
var filename = "data.txt"
}
class DataManager {
lazy var importer = DataImporter()
var data = [String]()
}
var manager = DataManager()
manager.data.append("data one")
manager.data.append("data two")
print(manager.importer.filename) // 第一次使用
当我们初始化变量manager
时,变量importer
事实上还没有被初始化,或者说不存在,只有第一次使用它时(即运行代码print(manager.importer.filename)
)它才会被初始化。这么做可以一定程度上减少计算资源的消耗。
计算属性
官方的定义如下:
In addition to stored properties, classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
可见计算属性是不存值的,只有在我们get
或set
这个变量的时候才会采取一些行动。首先来看看get
的用法:
struct Person {
let name: String
var age: Int
var type: String {
get {
if self.age > 18 {
return "adult"
} else {
return "teenager"
}
}
}
}
var person = Person(name: "Bob", age: 14)
print(person.type)
在变量person
初始化时,type
是没有初始化的,只有当运行代码print(person.type)
时,才会根据get
语句里的代码进行计算。这与下面的代码有根本性区别:
struct Person {
let name: String
var age: Int
var type: String { self.age > 18 ? "adult" : "teenager" }
}
var person = Person(name: "Bob", age: 14)
print(person.type)
这段代码里的type
在变量person
初始化的时候就已经计算出值了。接下来看看set
的用法:
struct Person {
let name: String
var age: Int
var type: String {
get {
if self.age > 18 {
return "adult"
} else {
return "teenager"
}
}
set (t) {
if t == "old" {
self.age = 65
}
}
}
}
var person = Person(name: "Bob", age: 14)
print(person.type)
person.type = "old"
print(person.age)
print(person.type)
以上代码的运行结果是:
teenager
65
adult
我们来分析一下过程。首先第一个print(person.type)
就不用分析了,直接来看person.type = "old"
这段代码。当运行这段代码时,触发了set
语句,把age
重新赋值为65
,但是第二个print(person.type)
的结果仍然是"adult"
而不是"old"
,是因为get
语句的作用。
属性观察者
Property observers observe and respond to changes in a property’s value. Property observers are called every time a property’s value is set, even if the new value is the same as the property’s current value.
当一个属性值被set
的时候,属性观察者会采取一些行动。属性观察者分为willSet
和didSet
两种:
- willSet is called just before the value is stored.
- didSet is called immediately after the new value is stored.
来看个例子:
struct Town {
var population: Int = 0{
willSet(newPopulation) {
print("小镇的人口马上要变成\(newPopulation)了!")
}
didSet {
if (population > oldValue) {
print("小镇的人口从原来的\(oldValue)增长了\(population - oldValue)!")
} else if population == oldValue {
print("小镇的人口没有增长,还是\(oldValue)。")
} else {
print("小镇的人口从原来的\(oldValue)减少了\(oldValue - population)!")
}
}
}
}
var town = Town()
town.population = 100
town.population = 500
town.population = 50
以上代码的运行结果是:
小镇的人口马上要变成100了!
小镇的人口从原来的0增长了100!
小镇的人口马上要变成500了!
小镇的人口从原来的100增长了400!
小镇的人口马上要变成50了!
小镇的人口从原来的500减少了450!
我们来逐行分析一下。
- 对于
town.population = 100
这段代码,首先运行willSet
中的代码,即打印小镇的人口马上要变成100了!
,由于100
大于初始值0
,所以运行didSet
中的代码,即打印小镇的人口从原来的0增长了100!
。 - 对于
town.population = 500
这段代码,首先运行willSet
中的代码,即打印小镇的人口马上要变成500了!
,由于500
大于100
,所以运行didSet
中的代码并且选择if (population > oldValue)
这个分支,即打印小镇的人口从原来的100增长了400!
。 - 对于
town.population = 50
这段代码,首先运行willSet
中的代码,即打印小镇的人口马上要变成50了!
,由于50
大于500
,所以运行didSet
中的代码并且选择else
这个分支,即打印小镇的人口从原来的100减少了450!
。
(未完待续)