作者:Thomas Hanning, 原文链接 ,原文日期:2018-03-15 译者: Sunnyyoung ;校对: 小铁匠Linus , numbbbbb ;定稿: Forelax
Swift 中有两种类型的属性:存储属性与计算属性。存储属性将值(常量或者变量)保存为实例或类型的一部分,而计算属性没有存储值。
提示:这篇文章已经更新至 Swift 4。
存储属性
让我们从存储属性开始看起。想象一下你有一个名为 Circle 的类:
class Circle {
var radius: Double = 0
}
let circle = Circle()
circle.radius = 10
print("radius: \(circle.radius)") //radius: 10.0
复制代码
Circle 拥有名为
radius
的实例变量,默认值为 0。在 Swift 中,每个实例变量都为一个属性。因此你可以添加所谓的属性观察者。在 Swift 中有两种类型的属性观察者:一种在赋值之前调用,另一种在赋值之后调用。
在赋值后调用的属性观察者采用
didSet
关键字标记。在我们的示例中,你可以使用它来监测新设置的值:
class Circle {
var radius: Double = 0 {
didSet {
if radius < 0 {
radius = oldValue
}
}
}
}
let circle = Circle()
circle.radius = -10
print("radius: \(circle.radius)") //radius: 0.0
circle.radius = 10
print("radius: \(circle.radius)") //radius: 10.0
复制代码
在属性观察者中你可以通过变量
oldValue
来访问属性的旧值。
你还可以使用
willSet
属性观察者,它在赋值之前会被调用:
class Circle {
var radius: Double = 0 {
willSet {
print("About to assign the new value \(newValue)")
}
didSet {
if radius < 0 {
radius = oldValue
}
}
}
}
let circle = Circle()
circle.radius = 10 //设置新值 10.0
复制代码
在
willSet
中,你可以通过变量
newValue
来访问属性的新值。
计算属性
与存储属性不同的是,计算属性并不会存储属性的值。因此在每次调用计算属性时,都要计算该值。在
Circle
类中,你可以将属性
area
定义为计算属性:
class Circle {
var radius: Double = 0 {
didSet {
if radius < 0 {
radius = oldValue
}
}
}
var area: Double {
get {
return radius * radius * Double.pi
}
}
}
let circle = Circle()
circle.radius = 5
print("area: \(circle.area)") //area: 78.5398163397448
复制代码
计算属性总是需要一个
getter
。如果缺少
setter
,则该属性被称为只读属性。下面这个例子很好地说明了
setter
的作用:
import Foundation
class Circle {
var radius: Double = 0 {
didSet {
if radius < 0 {
radius = oldValue
}
}
}
var area: Double {
get {
return radius * radius * Double.pi
}
set(newArea) {
radius = sqrt(newArea / Double.pi)
}
}
}
let circle = Circle()
circle.area = 25
print("radius: \(circle.radius)") //radius: 2.82094791773878
复制代码
至此,每次对 area 设置了新的值之后,radius 都会被重新计算。
存储属性的初始化
每个存储属性在它的对象实例化之后都必须有值。属性初始化有两种方法:
-
在
init
方法中初始化值 - 给属性设置默认的值
下面的例子同时使用了这两种方法:
class Circle {
var radius: Double
var identifier: Int = 0
init(radius: Double) {
self.radius = radius
}
}
var circle = Circle(radius: 5)
复制代码
如果存储属性在对象实例化之后没有值,代码无法通过编译。
懒加载属性
如果具有默认值的存储属性使用了关键字
lazy
标记,则其默认值不会立即初始化,而是在第一次访问该属性时初始化。
因此,如果该属性从未被访问,它将永远不会被初始化。你可以将这种特性应用于一些特别耗费 CPU 或内存的初始化上。
class TestClass {
lazy var testString: String = "TestString"
}
let testClass = TestClass()
print(testClass.testString) //TestString
复制代码
该属性在被访问之前不会进行初始化。在这个例子中并不容易看出来。但由于初始化也可以在 block 里面实现,我们可以使它更明显一些:
class TestClass {
lazy var testString: String = {
print("about to initialize the property")
return "TestString"
}()
}
let testClass = TestClass()
print