天天看点

swift基础day5---属性

属性

计算属性:直接计算(而不是存储)值,可以用于类、结构体和枚举。

存储属性:将常量和变量存储为实例的一部分,只能用于类和结构体。

类型属性:与类型本身关联。

  • 属性观察器可以添加到类本身定义的存储属性上,也可以添加到从父类继承的属性上。
  • 可以利用属性包装器来复用多个属性的 getter 和 setter 中的代码

    存储属性:

struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
//该区间表示整数0,1,2
rangeOfThreeItems.firstValue = 6  //该区间现在表示6,7,8
           

常量结构体实例的存储属性:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)   //该区间表示0,1,2,3
//rangeOfFourItems.firstValue = 6    //会报错
           

说明:由于结构体属于值类型,当值类型的实例被声明为常量的时候,它的所有属性也就成了常量。属于引用类型的类则不一样。把一个引用类型的实例赋给一个常量后,依然可以修改该实例的可变属性。

延时加载存储属性:

指当第一次被调用的时候才会计算其初始值的属性,用laze标识。必须将延时加载属性声明成变量而不是常量。

应用场景:(1)当属性的值依赖于一些外部因素且这些外部因素只有在构造过程结束之后才会知道的时候。(2)当获得属性的值因为需要复杂或者大量的计算,而需要 采用需要的时候再计算的方式。

class DataImporter {
    //DataImporter 是一个负责将外部文件中的数据导入的类,初始化会消耗不少时间
    var fileName = "data.txt"
    //提供数据导入功能
}
class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    //数据管理功能
}
let manager = DataManager()
manager.data.append("some data")
manager.data.append("some more data")
//DataImporter 实例还没有被创建
           

说明:使用了 lazy,DataImporter 的实例 importer 属性只有在第一次被访问的时候才被创建。比如访问它的属性 fileName 时

注:如果一个被标记为 lazy 的属性在没有初始化时就同时被多个线程访问,则无法保证该属性只会被初始化一次。

计算属性

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()
    //center的计算属性
    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) {
            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 initialSquaeCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
print("square.origin is now at (\(square.origin.x), \(square.origin.y))") //10.0
           

只读计算属性:只有getter,没有setter。get和花括号可以省略。

//只读计算属性
struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    var volume: Double{
        return width * height * depth
    }
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")  //40.0
           

属性观察器

属性观察器监控和响应属性值的变化,每次属性被设置值的时候都会调用属性观察器,即使新值和当前值相同的时候也不例外。

可以为属性添加一个或两个属性观察器:

  • willSet 在新的值被设置之前调用
  • didSet 在新的值被设置之后调用

    注:在父类初始化方法调用之后,在子类构造器中给父类的属性赋值时,会调用父类属性的 willSet 和 didSet 观察器。而在父类初始化方法调用之前,给子类的属性赋值时不会调用子类属性的观察器。

//属性观察器,willSet,didSet
class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("将 totalSteps 的值设置为 \(newTotalSteps)")
        }
        didSet {
            print("增加了 \(totalSteps - oldValue) 步")
        }
    }
}
let stepcounter = StepCounter()
stepcounter.totalSteps = 200   //增加了200步
stepcounter.totalSteps = 360    //增加了160步
stepcounter.totalSteps = 896    //增加了536步
           
属性包装器

属性包装器在管理属性如何存储和定义属性的代码之间添加了一个分隔层。

@propertyWrapper
struct TwelveOrLess {
    private var number = 0    //仅在TwelveOrLess中使用
    var wrappedValue: Int {
        get{ return number}
        set { number = min(newValue, 12)}
    }
}
struct SmallRectangle {
    @TwelveOrLess var height: Int
    @TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height)  //0
rectangle.height = 10
print(rectangle.height) //10
rectangle.height = 15
print(rectangle.height)  //12
           

从属性包装器中呈现一个值

@propertyWrapper
struct SmallNumber {
    private var maximum: Int
    private var number: Int
    
    var wrappedValue: Int {
        get { return number }
        set {
            number = min(newValue,maximum)
            print("set wrappedValue")
        }
    }
    //构造器
    init() {
        maximum = 12
        number = 0
        print("init()")
    }
    init(wrappedValue: Int){
        maximum = 12
        number = min(wrappedValue, maximum)
        print("inin(wrapped)")
    }
    init(wrappedValue: Int, maximum: Int){
        self.maximum = maximum
        number = min(wrappedValue, maximum)
        print("init(wrappedvlue,maximum)")
    }
}
//为设定初始值时
struct ZeroRectangle {
    @SmallNumber var height: Int
    @SmallNumber var width: Int
}
//使用init()
var zeroRectangle = ZeroRectangle()
print(zeroRectangle.height, zeroRectangle.width)  //0 0

//使用init(wrappedValue:)
struct UnitRectangle {
    @SmallNumber var height: Int = 1
    @SmallNumber var width: Int = 1
}
var unitRectangle = UnitRectangle()
print(unitRectangle.height, unitRectangle.width)  //1,1

//使用init(wrappedValue: maximum:)
struct NarrowRectangle{
    @SmallNumber(wrappedValue: 2, maximum: 5) var height: Int
    @SmallNumber(wrappedValue: 3, maximum: 4) var width: Int
}
var narrowRectangle = NarrowRectangle()
print(narrowRectangle.height, narrowRectangle.width)  //2 3
narrowRectangle.height = 10
narrowRectangle.width = 100
print(narrowRectangle.height, narrowRectangle.width)
           
全局变量和局部变量

全局变量是在函数、方法、闭包或任何类型之外定义的变量。

局部变量是在函数、方法或闭包内部定义的变量。

全局或局部变量都属于存储型变量,允许读写。

局部范围的常量和变量从不延迟计算。全局的常量或变量都是延迟计算的。

类型属性

类型属性的语法

使用关键字 static 来定义类型属性。在为类定义计算型类型属性时,可以改用关键字 class 来支持子类对父类的实现进行重写。

struct SomeStructure {
    static var storedTypeProperty = "some value."
    static var computedTypeProperty: Int{
        return 1
    }
}
enum SomeEnumeration {
    static var storedTyprProperty = "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 maxInputLevelForAllChanels = 0
    var currentLevel: Int = 0 {
        didSet{
            if currentLevel > AudioChannel.thresholdLevel {
                currentLevel = AudioChannel.thresholdLevel //将当前音量限制在阈值之内
            }
            if currentLevel > AudioChannel.maxInputLevelForAllChanels {
                AudioChannel.maxInputLevelForAllChanels = currentLevel
            }
        }
    }
}
var leftChannel = AudioChannel()
var rightchannel = AudioChannel()
leftChannel.currentLevel = 7
print(leftChannel.currentLevel)    //7
print(AudioChannel.maxInputLevelForAllChanels)  //7
rightchannel.currentLevel = 11
print(rightchannel.currentLevel)  //10
print(AudioChannel.maxInputLevelForAllChanels) //10