//属性
import UIKit
//属性(Properties)详解
/*
存储属性(Stored Properties):类、结构体中,不能在枚举中
计算属性(Computed Properties):类、结构体、枚举中,只能定义为变量
1.计算属性不直接存储值,而是提供一个 get{...return...} 和一个可选的 set(newValue){...},来间接获取和设置其他属性或变量的值。
2.必须定义为变量var,包括只读计算属性,因为它们的值不是固定的
延迟属性:当第一次被调用的时候才会计算其初始值的属性,用lazy来标示
1.必须将延迟存储属性声明成变量(var),因为属性的初始值可能在实例构造完成之后才会得到。而常量属性在构造过程完成之前必须要有初始值,因此无法声明成延迟属性。
2.使用情况:当属性的值依赖于在实例的构造过程结束后才会知道具体值的外部因素时,或者当获得属性的初始值需要复杂或大量计算时,可以只在需要的时候计算它
属性观察器(Property Observers):监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,甚至新值和当前值相同的时候也不例外。可以为除了延迟存储属性之外的其他存储属性添加属性观察器,也可以通过重写属性的方式为继承的属性(包括存储属性和计算属性)添加属性观察器
1.willSet在新的值被设置之前调用,将新的属性值作为常量参数传入,可以为该参数命名或者使用默认参数名newValue
2.didSet在新的值被设置之后立即调用,将旧的属性值作为参数传入,可以为该参数命名或者使用默认参数名oldValue
3.父类的属性在子类的构造器中被赋值时,它在父类中的willSet和didSet观察器会被调用
全局变量和局部变量(Global and Local Variables):
1.全局变量是在函数、方法、闭包或任何类型之外定义的变量。局部变量是在函数、方法或闭包内部定义的变量。
2.在全局或局部范围都可以定义计算型变量和为存储型变量定义观察器。计算型变量跟计算属性一样,返回一个计算结果而不是存储值,声明格式也完全一样。
3.全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记lazy修饰符,局部范围的常量或变量从不延迟计算
类型属性(Type Properties):声明关键字static,通过类型本身来调用,而非通过实例调用(C语言中称静态)
1.为某个类型本身定义的属性,用于定义某个类型所有实例共享的数据,无论创建多少个实例,这些类型属性都只有唯一的一份,可为常量或变量
2.必须给存储型类型的类型属性指定默认值,因为类型本身没有构造器,也就无法在初始化过程中使用构造器给类型属性赋值。
3.存储型类型属性是延迟初始化的,它们只有在第一次被访问的时候才会被初始化,即使它们被多个线程同时访问,系统也保证只会对其进行一次初始化,并且不需要对其使用lazy修饰符。
4.在为类定义计算型类型属性时,可以改用关键字class来支持子类对父类的实现进行重写
可延迟小结:
1.延迟属性,用lazy var定义的属性
2.自动闭包,延迟执行,只有被调用时才执行
3.全局变量或常量,是延迟计算的,不用lazy标记
4.存储型的类型属性,是延迟初始化的,第一次被访问时才会被初始化
5.指定清理操作:defer{...}内的语句的执行延迟到当前的作用域退出之前
*/
struct Point {
var x = 0.0, y = 0.0
}
struct Size {
var width = 0.0, height = 0.0
}
struct Cuboid {
lazy var yanchi = Point(x:1.0,y:2.0) //延迟属性,只有在被调用时才创建
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double { //属性volume为只读计算属性,它省略了get和{}
return width * height * depth
}
}
struct Rect {
var origin = Point()
var size = Size()
var center: Point { //计算属性必须声明为变量
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) { //set默认参数为(newValue),可省略,也可以自定义参数名
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( 10.0, height: 10.0))
let initialSquareCenter = square.center //调用get来获得值
square.center = Point(x: 15.0, y: 15.0) //调用set来设置值
print("square.origin is now at ((square.origin.x), (square.origin.y))")
// 输出 "square.origin is now at (10.0, 10.0)”
//=============观察器===================
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("更改之前为:(totalSteps) 更改之后为:(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("改完后的值为:(totalSteps),新值-旧值=(totalSteps - oldValue)")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200 //存储属性totalSteps的值被改为200之前先调用willSet,改完后调用didSet
stepCounter.totalSteps = 896
//==============类型属性=====================
struct SomeStructure {
static var storedTypeProperty = "Some value." //必须为所有的static的存储类型赋初始值
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int { //定义为在子类中可以将该属性重写
return 107
}
}
print(SomeStructure.storedTypeProperty) //类型属性是通过类型本身的点语法来访问,而不是通过实例
// 输出 "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// 输出 "Another value.”
print(SomeEnumeration.computedTypeProperty)
// 输出 "6"
print(SomeClass.computedTypeProperty)
// 输出 "27"
//==========
struct AudioChannel {
static let thresholdLevel = 10
static var maxInputLevelForAllChannels = 0
var currentLevel: Int = 0 {
didSet {
if currentLevel > AudioChannel.thresholdLevel {
currentLevel = AudioChannel.thresholdLevel
}
if currentLevel > AudioChannel.maxInputLevelForAllChannels {
AudioChannel.maxInputLevelForAllChannels = currentLevel
}
}
}
}
var leftChannel = AudioChannel()
var rightChannel = AudioChannel()
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// 输出 "7"
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "7"
rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// 输出 "10"
print(AudioChannel.maxInputLevelForAllChannels)
// 输出 "10"