Swift:Properties

属性是把值与特定的类,结构和枚举关联,存储属性存储实例中的常量和变量,而计算属性是计算某个值.计算属性由类,结构体和枚举提供,存储属性仅由类和结构体提供

存储和计算的属性通常与特定类型的实例相关联。然而,属性也可以与类型本身相关联。这些属性被称为类型属性

此外,也可以定义属性观察者去监听属性值的变化,针对于这一变化你可以用一个行为来回应此变化.属性观察者可以添加给存储属性,也可以添加到继承与它父类的子类中

Stored Properties
在最简单的形式中,存储的属性是一个常量或变量,它被存储为特定类或结构实例的一部分。存储的属性可以是可变的储存属性(由var关键字引入)或常量存储属性(由let关键字引入)。

您可以作为其定义的一部分,为存储的属性提供缺省值,如在默认属性值中所描述的那样。您还可以在初始化期间设定和修改存储属性的初始值。即使对于常量的存储属性也是如此,如在初始化期间分配常量属性所描述的那样

例如下面定义的FiexedLengthRange的结构体,这个结构体描述了整数的范围,这个范围的长度一旦被创建就不可改变

  struct FixedLengthRange {
     var firstValue : Int
     let length     : Int
}
var rangeOfThreeItems = FixedLengthRange(firstValue:0,length:3)
// the range represents integer values 0, 1, and 2
rangeOfThreeItems.firstValue = 6
// the range now represents integer values 6, 7, and 8

该结构体有一个变量存储属性firstValue和常量存储属性length,如上图中,当新的范围被创建时,长度被初始化,并且此后不能改变,因为它是一个常量属性

Stored Properties of Constant Structure Instances
如果你创建一个实例的结构,并将该实例分配给一个常数,您不能修改实例属性,即使他们声明为变量属性

“let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// this range represents integer values 0, 1, 2, and 3
rangeOfFourItems.firstValue = 6
// this will report an error, even though firstValue is a variable property

因为rangeoffofftems被声明为常量(带有let关键字),所以不可能改变firstValue属性,即使firstValue是一个可变属性
这种行为是由于结构是值类型(value types)的。当值类型的实例被标记为常量时,它的所有属性也是如此。

对于类来说,这是不正确的,它们是引用类型(reference type)。如果你将一个引用类型的实例赋给一个常量,你仍然可以改变那个实例的变量属性

Lazy Stored Properties
懒存储属性是它的初始值直到第一次使用时才计算的属性。通过在声明之前编写lazy修饰符,您可以指示一个懒存储属性
您必须始终将lazy属性声明为变量(带有var关键字),因为在实例初始化完成之后,它的初始值可能无法收回。恒定属性必须在初始化完成之前始终具有一个值,因此不能声明为lazy

当属性的初始值依赖于外部因素时,惰性属性是有用的,这些外部因素的值直到实例的初始化完成后才知道。当属性的初始值需要复杂的或计算昂贵的设置时,惰性属性也很有用,除非需要或在需要时才会执行

下面的例子使用惰性存储属性来避免复杂类不必要的初始化。这个例子定义了两个类称为DataImporter DataManager,既不全面所示

该类被认为需要花费大量的时间来初始化

class DataImporter {
    /*
     DataImporter is a class to import data from an external file.
     The class is assumed to take a non-trivial amount of time to initialize.
     */
    var fileName = "data.txt"
    // the DataImporter class would provide data importing functionality here
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()”
“    // the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
// the DataImporter instance for the importer property has not yet been created”

DataManager类有一个名为data的存储属性,它用一个新的空字符串数组来初始化。尽管它的其余功能没有显示出来,但是这个DataManager类的目的是管理和提供对这一系列字符串数据的访问
DataManager类的部分功能是能够从文件中导入数据。这个功能是由DataImporter类提供的,它被认为需要花费大量的时间来初始化。这可能是因为DataImporter实例需要打开一个文件,并在DataImporter实例被初始化时将其内容读入内存
DataManager实例可以不从文件导入数据就可以管理它的数据,因此当DataManager本身创建时,不需要创建新的DataImporter实例。相反,如果在第一次使用时创建DataImporter实例,则更有意义.
因为它是有lazy的修饰词,DataImporter实例属性只有importer创建的属性是首次访问,例如当其文件名属性查询

print(manager.importer.fileName)
// the DataImporter instance for the importer property has now been created
// Prints "data.txt”

注意:如果一个带有惰性修饰符的属性同时被多个线程访问,并且属性还没有被初始化,那么就不能保证该属性只会被初始化一次
Computed Properties
除了存储的属性之外,类、结构和枚举还可以定义计算的属性,这些属性实际上不存储值。而是,提供一个get和可选的set去间接地检索和设置其他属性

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

这个例子定义了三个结构体
Point 有(x,y)坐标
Size有(width,height)
Rect有原始Point值和size
Rect结构还提供了一个名为center的计算属性。矩形的当前中心位置总是可以根据它的原点和大小来确定,因此您不需要将中心点存储为显式的点值。相反,矩形定义了一个定制的getter和setter变量计算center,使您能够使用矩形中心就好像它是一个真正的存储属性
前面的例子创建了一个名为square的新Rect变量。平方变量的初始值为(0,0)的原点,宽度和高度为10。这个正方形由下面图中的蓝色方块表示
这里写图片描述
通过点语法(square.center)访问square的center属性,这个就是center的getter方法被调用,以检索到当前的属性值.不是返回一个已有的值,而是实际计算并返回一个新点来表示正方形的中心center point of (5, 5)

然后,中心属性被设置为(15,15)的新值,它将正方形向上和向右移动,到下图中橙色方块所示的新位置。设置中心属性调用center的setter,它修改存储源属性的x和y值,并将方块移动到它的新位置

如果一个计算属性的setter没有为将要设置的新值定义一个名称,那么就使用newValue的默认名称。

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 {
      origin.x = newValue.x - (size.width / 2)
      origin.y = newValue.y - (size.height / 2)
        }
    }
}

Read-Only Computed Properties
带有getter但没有setter的计算属性被称为只读计算属性。只读计算属性总是返回一个值,并且可以通过dot语法来访问,但是不能设置为不同的值
注意:
您必须声明计算属性,包括以var关键字作为变量属性的只读计算属性,因为它们的值不是固定的。let关键字只用于常量属性,以表明它们的值一旦被设定为实例初始化的一部分,就不能更改。
可以简化计算一个只读属性的声明通过移除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)")
// Prints "the volume of fourByFiveByTwo is 40.0

这个例子定义了一个名为Cuboid的新结构,它代表一个具有宽度、高度和深度属性的3D矩形框。这个结构也有一个称为卷的只读计算属性,它计算并返回cuboid的当前卷。对于卷的set来说,这是没有意义的,因为对于特定的卷值应该使用宽度、高度和深度的值,这是不明确的。尽管如此,它是有用的对于一个长方体提供只读计算属性,使外部用户能够发现其当前计算的卷

Property Observers
属性观察者观察并响应属性值的变化。财产观察员称为每次属性值设置,即使新值属性的当前值是一样的

除了lazy存储属性之外,您可以将属性观察器添加到您定义的任何存储属性中。您还可以通过子类重写属性来将属性观察者添加到任何继承的属性(无论是存储还是计算属性)。您不需要为非继承的计算属性定义属性观察者,因为您可以在计算属性的setter中观察和响应它们的值的变化。

你可以对一个属性定义1个或者两个观察者
willSet : 确定调用之前存储的值
didSet : 在新的值被存储之后立即调用

如果你实现了一个willSet观察者,它将新的属性值作为一个常量参数传递。您可以指定这个参数的名称作为willSet实现的一部分。如果您没有在实现中编写参数名和圆括号,那么参数就会以newValue的默认参数名可用
类似地,如果你实现了一个didSet观察者,它会传递一个包含旧属性值的常量参数。您可以命名参数或使用oldValue的默认参数名。

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
       print("About to set totalSteps to \(newTotalSteps)")
       }
        didSet {
            if totalSteps > oldValue  {
            print("Added \(totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps

StepCounter类声明了Int类型的totalSteps属性,这是willSet和didSet观测者的一个存储属性。
这个例子 willSet观察者使用一个自定义参数名newTotalSteps为即将到来的新值。在这个例子中,它只是输出的值将被设置。
翻译来自:
The Swift Programming Language(Swift 3)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值