前言
属性也叫类的成员变量,成员变量不能独立于类而存在,成员变量是描述类的对象的状态数据。
- 属性就是类所表示的现实对象的特性在代码中的反应。
- 在 Swift 语言中实例属性的类型分为两种,存储属性和计算属性。
- 在 Swift 语言中还有一种属性是类型属性,类型属性不属于任何一个类的实例,而是属于类本身。
1、存储属性
存储属性把常量或变量的值作为实例的一部分。
- 存储属性只能用于类和结构体里。
- 存储属性也分为变量属性和常量属性,分别用关键字
var
和关键字let
来描述。 - 在 OC 中,类的属性会有对应的实例变量,比如属性
a
对应的实例变量默认为_a
,而 Swift 中把二者统一了起来。
存储属性中的常量属性并不意味着绝对不能变化,而是要看情况。
- 如果存储属性的常量属性是值类型,比如字符串、结构体,就不能再变,结构体的属性也不能再变。
- 如果存储属性的常量属性是引用类型,如类,那么就可能重新赋值。
在 Swift 中,类在初始化的时候它的存储属性必须都被初始化。
- 如果初始化也不定义默认值,在调用
super.init()
时会出错,或者在声明了对象之后,也无法为此变量赋值。 如果不想设置某个属性的默认值,可以使用
?
把它加入可选链中,或者使用!
把它定义为隐式解包可选,也就是把它声明为可选型。class Student { var name: String? var age: Int = 10 }
- 如果初始化也不定义默认值,在调用
2、计算属性
计算属性本身并不直接存储特性,提供的是一个计算后的结果,类似于方法的功能,提供了一个处理数据的入口与出口。
- 计算属性可以用于类、枚举和结构体里。
- 计算属性(包含只读计算属性)都应该使用
var
关键字,因为他们的值并不是固定的。 在计算属性中可以使用
getter
和可选的setter
来间接的获得或者改变其他的属性和值。var/let 计算属性名称: 类型 { get { // 返回属性的值 } set(newValue) { // 执行赋值操作 } }
属性类型
- 使用计算属性时一定要声明属性的类型,否则编译器会报错。
get 方法
- 在计算属性中可以定义
get
方法和set
方法,其中set
方法不是必须的。 - 如果计算属性中没有写
set
方法,则可以把get
方法中的内容直接写到计算属性的方法体中,从而省略外面的get{}
。
- 在计算属性中可以定义
set 方法
- 在计算属性中可以定义
get
方法和set
方法,其中set
方法不是必须的。 set
方法有一个默认的参数newValue
,用来表示传入的新值。- 即便不在参数列表中显示的写出
newValue
,依旧可以在set
方法中使用newValue
。 - 可以在参数列表中写上其它名字以替换默认的参数名
newValue
。
- 在计算属性中可以定义
定义计算属性时直接在属性类型后面的大括号
{}
中设置set
和get
方法,不需要设置初始值。class Student { var givenName = "" var firstName = "" var allName: String { get { return givenName + firstName } set { firstName = newValue } } }
3、类型属性
可以定义一种属性,这种属性不属于任何一个类的实例,即不属于任何一个对象,它只属于类本身,这样的属性就称为类型属性。
- 即使创建再多的这个类的实例,这个属性也不属于任何一个,它只属于类本身。
- 类型属性可以用于定义特定类型所有实例共享的数据。
- 类型属性可以包括存储属性和计算属性。
- 使用关键字
static
定来义类型属性。 - 在类中用关键字
class
来定义被重写的计算属性。 - 类型属性在使用时直接使用类型本身来调用。
类中的类型属性定义示例
class NewClass { // 定义存储类型属性 static var storedTypeProperty = "someValue" // 定义计算类型属性 static var computedTypeProperty: Int { // get、set 方法 return 0 } // 重写计算类型属性 class var overrideableComputedTypeProperty: Int { // get、set 方法 return 1 } } print(NewClass.storedTypeProperty) // "someValue" print(NewClass.computedTypeProperty) // 0 print(NewClass.overrideableComputedTypeProperty) // 1
结构体中的类型属性示例
struct NewStruct { // 定义存储类型属性 static var storedTypeProperty = "someValue" // 定义计算类型属性 static var computedTypeProperty: Int { // get、set 方法 return 0 } } print(NewStruct.storedTypeProperty) // "someValue" print(NewStruct.computedTypeProperty) // 0
枚举中的类型属性示例
enum NewEnum { // 定义存储类型属性 static var storedTypeProperty = "someValue" // 定义计算类型属性 static var computedTypeProperty: Int { // get、set 方法 return 0 } } print(NewEnum.storedTypeProperty) // "someValue" print(NewEnum.computedTypeProperty) // 0
4、懒加载
存储属性一般要求初始化,我们可以设置一个
lazy
修饰符,使得这种初始化被延迟,尽管我们在属性声明时已经做了这种初始化上的定义。lazy
修饰的存储属性叫 “懒惰存储属性(懒加载)”。- 懒惰存储属性的初始值直到实例初始化完成之后,在调用之时才被计算。
- 懒惰存储属性必须是变量属性,即用
var
定义的属性,常量属性不能够被声明为懒惰存储属性,常量属性在实例初始化完成之前就应该被赋值。 - 懒惰存储属性依旧遵循类在初始化的时候所有属性必须初始化的规则,并且必须设置默认值。
在全局变量中不需要显式的指定 lazy 属性,而是所有的全局变量都是延迟计算的。
lazy var name: String? = "xiaoming"
lazy var shops: NSArray = { let filePath: String = Bundle.main.path(forResource: "shops", ofType: "plist")! let tmp = NSArray(contentsOfFile: filePath)! return tmp }()
所谓修饰符就是加在变量、函数、类等的定义前面,用来约束或者增强变量、函数、类功能的一种特殊的关键字。比如修饰符
@objc
表示导出给 OC 调用。
5、属性观察者
属性观察者更像是触发器,可以用来观察属性值的变化,不过属性观察者同触发器不同的是,还可以在属性变更前触发。
- 属性观察者观察属性值的改变并对此作出响应。
- 当设置属性的值时,属性观察者会被调用,即使当新值同原值相同时也会被调用。
- 除了懒惰存储属性,你可以为任何存储属性加上属性观察者定义。
- 通过重写子类型属性,也可以为继承的属性(存储或计算)加上属性观察者定义。
属性观察者更适合值类型,属性观察者实际上监听的是 “栈” 上所发生的改变。
var/let 属性名: 类型 = 默认值 { willSet { // 在属性更改之前做某些操作 } didSet { // 在属性更改之后做某些操作 } }
属性类型
- 给属性添加观察者时必须要声明清楚属性的类型,否则编译器报错。
willSet 方法
willSet
方法有一个默认的参数newValue
,用来表示传入的新值。- 即便不在参数列表中显示的写出
newValue
,依旧可以在willSet
方法中使用newValue
。 - 可以在参数列表中写上其它名字以替换默认的参数名
newValue
。
didSet 方法
didSet
方法有一个默认的参数oldValue
,用来表示之前的旧值。- 即便不在参数列表中显示的写出
oldValue
,依旧可以在didSet
方法中使用oldValue
。 - 可以在参数列表中写上其它名字以替换默认的参数名
oldValue
。
属性初始化时,
willSet
和didSet
并不会被调用。只有在初始化上下文时,当设置属性值时才被调用。willSet
和didSet
每次设置属性值都会被调用,即使是设置的值和原来的值相同。class Student { var name: String = "xiaoming" var age: Int = 0 { willSet { print(name + "old: \(age)") print(name + "new: \(newValue)") } didSet { print(name + "old: \(oldValue)") print(name + "new: \(age)") } } }
在 iOS 开发中属性观察者常用的地方就是更新用户界面(UI),比如对页面上的某些属性做了修改,那么就需要早
didSet
方法中对用户界面进行更新。