- 属性分为类型属性和实例属性。使用
static
和class
关键字修饰的属性为类型属性,否则为实例属性。其中static
和class
两个关键字的区别在于,static
修饰的类型属性不能重载。而class
属修饰的类型属性在子类中可以使用override class
来进行重载。 - 属性分为存储属性和计算属性两种,类和结构体都可以拥有这两种属性,但是枚举只能有计算属性。
存储属性用来例如控件创建等,特点是有
=
。而计算属性是用来获取计算值,特点是get
和set
。 - 不同于OC中的属性和实例变量的两个概念,swift中只有属性。实际上swift中有存储属性和计算属性,存储属性用来存储相应值,而计算属性会提供一个
getter
方法,和一个可选的setter
方法。 - 可以使用
let
常量和var
变量来修饰属性,其中变量属性可以随时修改,而常量属性只能在初始化的时候赋值。注意:计算属性,包括只读计算属性,都只能用
var
来修饰。 - 结构体的实例需要定义成变量才可以修改其变量属性,否则会报错。而类的实例不需要定义成变量(即常量情况下)也可以修改其变量属性。,区别原因的根本在于值类型和引用类型的不同。
- 变量总需要使用lazy定义为延迟属性,因为变量可以在初始化过程中不赋值,而是在初始化结束赋值并使用。而常量不可以定义为延迟属性,因为常量在初始化过程中就赋值完成了。
- 使用lazy实现存储属性的懒加载
//Lazy Stored Properities class DataImporter { var fileName = "data.txt" } class DataManager { lazy var importer = DataImporter() var data = [String]() } //直到这里DataImporter()才会执行。 print(manager.importer.filename) // the DataImporter instance for the importer property has now been created // Prints "data.txt"
注意:多线程同时访问未被初始化的lazy属性时,可能会出现多次初始化的问题
- 计算属性不直接储存值,而是通过
getter
方法通过其他存储属性计算返回值,通过setter
方法计算赋给其他存储属性值
可以在set后的括号中指定set方法内的形参名,也可以不指定,使用默认newValuestruct Rect { var origin = Point() var size = Size() //不需要存储center的值,而是根据origin和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) } } }
注意:属性,例如view中的控件,应该用存储属性,不应该用计算属性
lazy var loginButton: UIButton = { let button = UIButton() button.backgroundColor = UIColor.red return button }()
- 只读计算属性,括号内没有
setter
只有getter
方法,也可以省略get
关键字,简写成如下,直接返回计算值:struct Cuboid { var width = 0.0, height = 0.0, depth = 0.0 var volume: Double { return width * height * depth } }
- 属性监视器,监测储存属性变化,用来检测存储属性(懒加载的存储属性除外)。可以为重载的计算属性添加属性监视器,不需要对无法重载的计算属性添加属性监视器。使用willSet在赋值前监测,使用didSet在赋值后监测
class StepCounter { var totalSteps: Int = 0 { willSet(newTotalSteps) { print("About to set totalSteps to \(newTotalSteps)") } didSet { if totalSteps >= oldValue { print("Added \(totalSteps - oldValue) steps") } } } }
注意:
willSet
和didSet
在构造器中初始化的时候不会被调用,只有在本类构造器外的地方被设定值时才会触发。包括其子类的构造器中,设定值就可以触发。 - 被监测的属性以
inout
类型作为函数参数时,willSet
和didSet
在函数结尾总会被调用(传入时不确定),是因为inout
的拷贝原理导致的 - 全局变量会自动延迟计算,和延迟属性类似,不过不需要使用
lazy
关键字,但是局部变量不会延迟计算 - 类型属性,使用
static
关键字修饰类型属性。类似于C语言的静态变量和常量,应用于类、结构体、枚举。当声明了类型属性后,该类的所有实例均有共同的此属性变量值。区别于实例属性,实例属性在创建的每一个实例中都有一套不同的属性值。
存储类型属性可以是let
或者var
,计算类型属性只能是var
,这跟普通实例属性一致。struct SomeStructure { //使用static关键字 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关键字表示可被子类重写 class var overrideableComputedTypeProperty: Int { return 107 } }
类型属性必须有默认值,因为类型本身没构造器。类型属性为懒加载类型,只会执行一次,即使在多个线程中,且不需要
lazy
关键字。 - 类型属性的读写使用点语法,与实例属性基本一致,区别在于作用于类型而不是实例。
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"
- 使用类型属性来描述类型的通用属性值,可以随着某一实例变化而影响这个值。例如角色最高得分,其中某一角色得到最高分的时候会刷新所有角色能看到的最高分。一个具体的例子如下:
struct AudioChannel { //定义等级上限 static let thresholdLevel = 10 //定义当前最高等级 static var maxInputLevelForAllChannels = 0 var currentLevel: Int = 0 { didSet { //如果超过等级上限取等级上限 if currentLevel > AudioChannel.thresholdLevel { // cap the new audio level to the threshold level currentLevel = AudioChannel.thresholdLevel } //超过当前最高等级,刷新当前最高等级 if currentLevel > AudioChannel.maxInputLevelForAllChannels { // store this as the new overall maximum input level AudioChannel.maxInputLevelForAllChannels = currentLevel } } } } var leftChannel = AudioChannel() var rightChannel = AudioChannel() //左通道刷新最高等级为7 leftChannel.currentLevel = 7 print(leftChannel.currentLevel) // Prints "7" print(AudioChannel.maxInputLevelForAllChannels) // Prints "7" //右通道刷新最高等级为10 rightChannel.currentLevel = 11 print(rightChannel.currentLevel) // Prints "10" print(AudioChannel.maxInputLevelForAllChannels) // Prints "10"
网友评论