寒城攻略:Listo 教你 25 天学会 Swift 语言 - 12 Properties

时间:2021-11-27 19:36:46

import Foundation


//***********************************************************************************************

//1.Properties(属性)

//_______________________________________________________________________________________________

//介绍

//属性将值跟特定的类,结构或者枚举关联。存储属性 存储常量或变量作为实例的一部分,计算属性 计算一个值,计算属性可以用于类,结构体,枚举中,存储属性只能用在类和结构体中

//存储属性 计算属性 通常用于特定类型的实例,但是属性也可以直接用于类型本身,这种属性称为 类型属性

//此外,我们还可以定义 属性监视器 来监控属性的变化,以此来触发一个自定义的操作。属性监视器可以添加到自己写的属性上,也可以添加到从父类继承的属性上


//***********************************************************************************************

//2.Stored Properties(存储属性)

//_______________________________________________________________________________________________

//介绍

//简单来说,一个存储属性就是存储在特定类或结构体的实例里的一个常量或变量,存储属性可以是变量存储属性(用关键字 var 定义),也可以是常量存储属性(用关键字 let 定义)


//_______________________________________________________________________________________________

//代码演示存储属性

struct FixedLengthRange{        //FixedLengthRange 的实例包含一个名为 firstValue 的变量存储属性和一个名为 length 的常量存储属性

    var firstValue: Int

    let length: Int

}

var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)

rangeOfThreeItems.firstValue = 6


//_______________________________________________________________________________________________

//常量和存储属性

let rangeOfItems1 = FixedLengthRange(firstValue: 0, length: 4)      //这里如果定义一个常量为类实例 rangeOfItems1,那么不管属性是否为常量或者变量,属性都不可以再次修改

// rangeOfThreeItems1.firstValue = 6            //如果修改属性 firstValue,系统报错。这样的原因是因为结构体属于值类型,所以在定义实例的时候如果为常量,那么所有的属性均为常量,如果是类这种引用类型,那么即使定义实例为常量,属性依旧可以是变量可修改


//_______________________________________________________________________________________________

//延迟存储属性

//介绍

//延迟存储属性是指当第一次被调用的时候才会计算其初始值的属性,在属性声明前使用 @lazy 来标示一个延迟存储属性

//注意必须将延迟存储属性声明为变量,因为属性的值在实例构造完成之前可能无法得到。而常量属性在构造的过程完成前必须要有初始值,因此无法声明为延迟属性

//延迟属性很有用,当属性的值依赖于在实例的构造过程结束前无法知道具体指的外部因素时,或者当属性的值需要大量计算时,可以只在需要计算的时候来计算它即可


//代码演示延迟属性

class DataImporter{

    /*

    DataImporter 是一个将外部文件中的数据导入的类。

    这个类的初始化会消耗不少时间。

    */

    var fileName = "data.txt"       //这里提供数据导入功能

}


class DataManager{

        @lazy var importer = DataImporter()

        var data = [String]()       //这时提供数据管理功能

}

let manager = DataManager()

manager.data += "some data"

manager.data += "some more data"        //DataImporter 实例的 importer 属性还没有被创建

println(manager.data)

println(manager.importer.fileName)      //由于使用了 @lazy importer 属性只有在第一次被访问时才被创建,例如访问他的 fileName 属性


//***********************************************************************************************

//3.Computed Properties(计算属性)

//_______________________________________________________________________________________________

//介绍

//计算属性不直接存储值,而是提供一个 getter 来获取值,一个可选的 setter 来间接的设置其他属性或者变量的值


//_______________________________________________________________________________________________

//代码演示计算属性

struct Point{               //Point 表示一个坐标

    var x = 0.0

    var y = 0.0

}

struct Size{                //Size 表示长宽尺寸

    var width = 0.0

    var height = 0.0

}

struct Rect{                //Rect 表示一个有原点和尺寸的矩形

    var origin = Point()

    var size = Size()

    var center: Point{      //Rect 提供了一个名为 center 的计算属性

        get{

            let centerX = origin.x + (size.width / 2)

            let centerY = origin.y + (size.height / 2)

            return Point(x: centerX, y: centerY)        //计算矩形的中心点

        }

        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

println("(\(initialSquareCenter.x), \(initialSquareCenter.y))")     //访问属性通过 get 获取计算属性的值


square.center = Point(x: 15.0, y: 15.0)         //center 属性通过 set 被设置为新的值

println("square.origin is now at (\(square.origin.x), \(square.origin.y))")


//_______________________________________________________________________________________________

//便捷 setter 声明

struct AlternativeRect{

    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{        //如果计算属性的 setter 没有定义表示新值的参数名,可以使用默认的 newValue 来便捷设置

            origin.x = newValue.x - (size.width / 2)        //这里的 newVlaue 就是我们设置的 set

            origin.y = newValue.y - (size.height / 2)

        }

    }

}


//_______________________________________________________________________________________________

//只读计算属性(只有 getter 没有 setter 的属性就叫只读计算属性)

//必须使用 var 来定义计算属性,包括只读计算属性

struct Cuboid{          //只读计算属性可以省略 get 关键字和大括号

    var width = 0.0

    var height = 0.0

    var depth = 0.0

    var volume: Double{

        return width * height * depth

    }

}

let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)

println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")


//***********************************************************************************************

//4.Property Observers(属性监视器)

//_______________________________________________________________________________________________

//代码实例演示属性监视器

//属性监视器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性监视器,甚至 新的值和现在的值相同的时候也不例外

class StepCounter{

    var totalSteps: Int = 0{

    willSet(newTotalSteps){

        println("About to set totalSteps to \(newTotalSteps)")

    }

    didSet{

    if totalSteps > oldValue{

        println("Added \(totalSteps - oldValue) steps")

        }

    }

    }

}

let stepCounter = StepCounter()

stepCounter.totalSteps = 200

stepCounter.totalSteps = 360

stepCounter.totalSteps = 896        //每当 StepCounter 实例调用 totalSteps 属性的时候,属性监视器就会被触发运行


//***********************************************************************************************

//5.Global and Local Variables(全局变量和局部变量)

//_______________________________________________________________________________________________

//介绍

//计算属性和属性监视器所描述的模式也可以用于全局变量和局部变量,全局变量是在函数、方法、闭包或任何类型之外定义的变量,局部变量是在函数、方法或闭包内部定义的变量

//全局的常量或变量都是延迟计算的,跟延迟存储属性相似,不同的地方在于,全局的常量或变量不需要标记@lazy 特性;局部范围的常量或变量不会延迟计算


//***********************************************************************************************

//6.Type Properties(类型属性)

//_______________________________________________________________________________________________

//介绍

//类型的属性属于一个特定类型实例,每次类型实例化后都拥有自己的一套属性值,实例之间的属性是相互独立的


//_______________________________________________________________________________________________

//类型属性的语法(使用 static 来定义值类型的类型属性,使用 class 来定义类的类型属性)

struct SomeStructure{

    static var storedTypeProperty = "some Value"

    static var computedTypeProperty: Int{

        return 9

    }

}


enum SomeEnumeration{

    static var storedTypeProperty = "some Value"

    static var computedTypeProperty: Int{

        return 9

    }

}


class SomeClass{

    class var computedTypeProperty: Int{

        return 9

    }

}


//_______________________________________________________________________________________________

//获取和设置类型属性的值(和实例的属性一样,类型属性的访问也是通过点运算符来进行,但是类型属性是通过类型本身来获取和设置,而不是通过实例)

println(SomeClass.computedTypeProperty)

println(SomeStructure.computedTypeProperty)

println(SomeEnumeration.computedTypeProperty)       //直接获取类型属性


SomeStructure.storedTypeProperty = "another Value"      //直接修改类型属性

println(SomeStructure.storedTypeProperty)


//_______________________________________________________________________________________________

//类型属性代码实例

struct AudioChannel{                //结构 AudioChannel 定义了 2 个存储型类型属性来实现上述功能

    static let thresholdLevel = 10

    static var maxInputLevelForAllChannels = 0

    var currentLevel: Int = 0{

    didSet{     //属性 currentLevel 包含 didSet 属性监视器来检查每次新设置后的属性值,有如下两个检查

        if currentLevel > AudioChannel.thresholdLevel{          //第一个是 thresholdLevel 表示声音电平的最大上限阈值,它是一个取值为 10 的常量,对所有实例都可见,如果声音电平高于 10,则取最大上限值 10

            currentLevel = AudioChannel.thresholdLevel

        }

        if currentLevel > AudioChannel.maxInputLevelForAllChannels{         //第二个类型属性是变量存储型属性 maxInputLevelForAllChannels,它用来表示所有 AudioChannel 实例的电平值的最大值,初始值是 0

            AudioChannel.maxInputLevelForAllChannels = currentLevel

        }

    }

    }

}

var leftChannel = AudioChannel()

var rightChannel = AudioChannel()       //可以使用结构体 AudioChannel 来创建表示立体声系统的两个声道 leftChannel rightChannel


leftChannel.currentLevel = 7

println(AudioChannel.maxInputLevelForAllChannels)       //直接访问属性不需要实例,这就是类型属性


rightChannel.currentLevel = 11

println(rightChannel.currentLevel)

println(AudioChannel.maxInputLevelForAllChannels)