Swift中,属性大致分为两种,一种叫存储属性,一种叫计算属性。
所谓的存储属性,就是能保存数据的属性。枚举类、结构体、类都具有存储属性。
计算属性就是它本身不保存数据,而是通过处理其他的数据得到返回值。 结构体和类才具有计算属性。
存储属性(Stored Properties)
先来看存储属性,存储属性可以设为var 或let。分别对应常量和变量。
struct CPU {
var clockSpeed = 1.0 //变量
let coreNum = 2 //常量
}
然后我们初始化一个CPU实例,但是这个实例我们设置为常量。这样会导致一个后果:对应值传递的结构体,当实例是常量的时候,即使存储属性是变量,也不能修改里面属性的值。
let cpu = CPU(clockSpeed: 3.2) //注意到这个初始化方法只有一个参数,因为我们设置了coreNum为常量,并且给了初始值,所以构造方法会发生变化
cpu.clockSpeed = 3.3 //这句会报错
class Telephone {
var cpu = CPU()
var screenSize: Int?
var price: Int?
}
let myTelephont = Telephone()
myTelephont.screenSize = 4
懒加载存储属性
这里先提早说一下,Swift需要保证一个类或结构体或枚举类在初始化之后,所以的存储属性都必须有初始值(除开可选类型)。所以当实例初始化完成以后,所以的存储属性都初始化了。但是有些时候某些存储属性不一定会用到,所以我们可以把这些存储属性设置为lazy存储属性。那么它在要用到的时候才会进行初始化。lazy存储属性只能是变量。而且在多线程调用的时候,如果lazy属性没有初始化,那么不能保证只会初始化一次。
struct CPU {
lazy var clockSpeed = 1.0
let coreNum = 2
}
let cpu = CPU() //这时候clockSpeed还是为nil
计算属性(Computed Properties)
故名思议,它本身不保存数据,只是用其他数据得到返回值,或者当设置它的时候修改其他数据。下面的例子利用存储属性得到apple的总价。或者修改总价的时候修改重量。
struct Apple {
let pricePerKg = 6.0
var weight: Double
var totalPrice: Double {
get {
return pricePerKg * weight
}
set(newTotalPrice) {
weight = newTotalPrice / pricePerKg
}
}
}
var apple = Apple(weight: 2.0)
print(apple.totalPrice) //12.0
apple.totalPrice = 6.0
print(apple.weight) //1.0
计算属性的setter方法新值自带一个默认名 newValue,所以上面的定义可以改为
struct Apple {
let pricePerKg = 6.0
var weight: Double
var totalPrice: Double {
get {
return pricePerKg * weight
}
set {
weight = newValue / pricePerKg
}
}
}
你还可以将计算属性的setter去掉,那么这个计算数据就变成了只读的。这时候还可以省去get关键字
struct Apple {
let pricePerKg = 6.0
var weight: Double
var totalPrice: Double {
return pricePerKg * weight
}
}
注意一点,不要在计算属性的setter和getter里面获取该计算属性,这样会导致循环调用。
属性观察器(Property Observers)
有了属性观察器,我们可以在属性被修改的时候做一些事情。注意的是对于非继承而来的计算属性,没有必要设置属性观察器,因为直接可以在计算属性的setter定义中完成这项功能。
属性观察器有两个方法
willSet 在属性将要赋值的时候调用,这时候属性的值还没改变
didSet在属性值被赋值完的时候就会调用。(即使是赋值和原来值一样的值)这时候属性的值已经改变。
willSet方法里面会传递新值过来,你可以自己定义这个新值的名称,如果不定义,会具有默认值newValue
didSet方法里面会传递属性的旧值过来,你可以自己定义这个旧值的名称,如果不定义,会具有默认值oldValue。如果你在didSet方法里面又对这个存储属性赋值,那么这个值会覆盖掉刚刚赋值的值。且不会导致循环调用。比如下面例子里面,当pricePerKg小于3的时候,会把它改为3.
struct Apple {
var pricePerKg = 6.0 {
willSet(priceNewValue){
print("In willSet, priceNewValue=",priceNewValue)
print("In willSet, pricePerKg=",pricePerKg)
}
didSet{
print("In didSet, oldValue=",oldValue)
print("In didSet, pricePerKg=",pricePerKg)
if pricePerKg < 3 {
pricePerKg = 3
}
}
}
var weight: Double
var totalPrice: Double {
get {
return pricePerKg * weight
}
set {
weight = newValue / pricePerKg
}
}
}
var apple = Apple(pricePerKg: 6.0, weight: 1.0)
apple.pricePerKg = 2.0
print("apple.pricePerKg=",apple.pricePerKg)
/* 输出
In willSet, priceNewValue= 2.0
In willSet, pricePerKg= 6.0
In didSet, oldValue= 6.0
In didSet, pricePerKg= 2.0
apple.pricePerKg= 3.0
*/
Global and Local Variables
)所谓的局部变量,就是定义在函数、方法、闭包、类型上下文里面的变量。
全局变量就是定义在函数、方法、闭包、类型上下文之外的变量。
局部变量和全局变量都称为存储变量。
在全局和局部范围内,都可以定义计算变量,或者为存储变量定义观察器。
var apple = Apple(pricePerKg: 6.0, weight: 1.0) { //为apple变量定义观察器
willSet {
print("newApple",newValue)
}
didSet{
print("oldApple",oldValue)
}
}
apple = Apple(pricePerKg: 6.0, weight: 2.0)
var c: Double { //定义一个计算变量
get{
return 9
}
set {
print("In setter,",newValue)
}
}
c = 2
print(c)
/*
newApple Apple(pricePerKg: 6.0, weight: 2.0)
oldApple Apple(pricePerKg: 6.0, weight: 1.0)
In setter, 2.0
9.0
*/
官方文档说全局变量常量都是懒加载,局部变量常量都不是懒加载。 这一点我还没验证出来
Type Properties
)所谓的类型属性,就是这个属性是属于这个类型的,即使这个类型由多个实例,也只会有一个类型变量。
类型属性是懒加载的,而且必须在定义的时候给初始值。
一般是用static来定义类型属性。特别的,对应类的计算类型属性,还可以用class关键字定义,这样可以使得子类可以覆盖它。
注意类型属性访问的时候用的是类名,不能使用实例来访问。
struct SomeStructure {
static var storedTypeProperty = "Some value."
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)
// prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty)
// prints "Another value."
print(SomeEnumeration.computedTypeProperty)
// prints "6"
print(SomeClass.computedTypeProperty)
// prints "27"