计算属性和存储属性
我们知道在OC中, 使用@ property声明的实例变量, 都会自动生成setter和getter方法, 但是在swift中, 属性分为计算属性和存储属性, 存储属性存储常量或变量作为实例的一部分,计算属性计算(而不是存储)一个值,就是setter和getter方法, 除存储属性外,类、结构体和枚举可以定义计算属性,计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接设置其他属性或变量的值。
延迟存储属性
延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性。在属性声明前使用lazy(xcode8)不是@lazy来标示一个延迟存储属性。类似于OC中的懒加载
注意:必须将延迟存储属性声明成变量(使用var关键字),因为属性的值在实例构造完成之前可能无法得到(意思就是说, 延迟属性, 不需要在创建实例时赋初始值)。而常量(使用var)属性在构造过程完成之前必须要有初始值(如果你不给初始值, 编译器会根据类型推断, 自动分配),因此无法声明成延迟属性。
class DataImporter {
/*
DataImporter 是一个将外部文件中的数据导入的类。
这个类的初始化会消耗不少时间。
*/
var fileName = "data.txt"
// 这是提供数据导入功能
}
class DataManager {
lazy var importer = DataImporter() //延迟属性, 用到的时候才回去加载
var data = String[]()
// 这是提供数据管理功能
}
计算属性
//结构体
struct Point {
var x = 0.0, y = 0.0
}
//结构体
struct Size {
var width = 0.0, height = 0.0
}
//结构体
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
//getter方法
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
//setter方法, newCenter为setter方法的参数
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))
let initialSquareCenter = square.center //getter方法, 获取center
square.center = Point(x: 15.0, y: 15.0) //setter方法, 设置新的center
print("square.origin is now at (\\(square.origin.x), \\(square.origin.y))")
只读计算属性
只有 getter 没有 setter 的计算属性就是只读计算属性。只读计算属性总是返回一个值,可以通过点运算符访问,但不能设置新的值。(类比OC属性修饰readonly)
注意:必须使用var关键字定义计算属性,包括只读计算属性,因为他们的值不是固定的。let关键字只用来声明常量属性,表示初始化后再也无法修改的值。
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double { //只读计算属性的声明可以去掉get关键字和花括号:
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \\(fourByFiveByTwo.volume)")
属性监视器
属性监视器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器,甚至新的值和现在的值相同的时候也不例外. (类似于KVO, 但是比KVO方便多了)
可以为属性添加如下的一个或全部监视器:
willSet在设置新的值之前调用
didSet在新的值被设置之后立即调用
willSet监视器会将新的属性值作为固定参数传入,在willSet的实现代码中可以为这个参数指定一个名称,如果不指定则参数仍然可用,这时使用默认名称newValue表示。
类似地,didSet监视器会将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue。
注意:willSet和didSet监视器在属性初始化过程中不会被调用,他们只会当属性的值在初始化之外的地方被设置时被调用。
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) { //willSet监视器将表示新值的参数自定义为newTotalSteps
print("About to set totalSteps to \\(newTotalSteps)")
}
didSet {
if totalSteps > oldValue { //didSet没有提供自定义名称,所以默认值oldValue表示旧值的参数名。
print("Added \\(totalSteps - oldValue) steps")
//注意:如果在didSet监视器里为属性赋值,这个值会替换监视器之前设置的值。
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200 //设置新值, 调用willSet
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
//
类型属性
实例的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性相互独立。
也可以为类型本身定义属性,不管类型有多少个实例,这些属性都只有唯一一份。这种属性就是类型属性。
类型属性用于定义特定类型所有实例共享的数据,比如所有实例都能用的一个常量(就像 C 语言中的静态常量),或者所有实例都能访问的一个变量(就像 C 语言中的静态变量)。
对于值类型(指结构体和枚举)可以定义存储型和计算型类型属性,对于类(class)则只能定义计算型类型属性。
值类型的存储型类型属性可以是变量或常量,计算型类型属性跟实例的计算属性一样定义成变量属性。
注意:跟实例的存储属性不同,必须给存储型类型属性指定默认值,因为类型本身无法在初始化过程中使用构造器给类型属性赋值。
//结构体
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 10
// 这里返回一个 Int 值
}
}
//枚举
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// 这里返回一个 Int 值
return 20
}
}
//类
class SomeClass {
//这里在xcode8里, class改为static也可以(目前测试, 结果都是对的, 可能还有没有考虑到的情况)
class var computedTypeProperty: Int {
// 这里返回一个 Int 值
return 30
}
}
print(SomeStructure.computedTypeProperty) //10
print(SomeStructure.computedTypeProperty) //20
print(SomeClass.computedTypeProperty) //30