Swift 属性
我们可以定义属性来将值与类、结构和枚举关联起来。在Swift中,我们可以定义两种类型的属性,即存储属性和计算属性。存储属性将变量和常量值存储为类属性。而计算属性根据特定值计算值。我们可以在类、结构和枚举中定义计算属性;但是,存储属性只能在类和结构中定义。
特定类型的实例,包括结构、类和枚举,与类型中定义的所有属性关联。但是,属性也可以与类型本身关联,而不是实例。这种属性被称为类型属性。
在本教程的这一部分,我们将讨论各种类型的Swift属性,如存储属性和计算属性。
存储属性
简单地说,存储属性是作为特定类或结构实例的一部分定义的常量或变量。存储属性可以是常量(使用let关键字定义)或变量(使用var关键字定义)。我们可以在声明存储属性时为其提供默认值。然而,在初始化类或结构时,我们可以修改存储属性的默认值。
让我们来考虑下面的示例,它定义了一个名为Person的类。它包含变量存储属性name和age。每个人都有一个默认的姓名和年龄,可以在初始化时更改。
class Person{
var name:String = "Anonymous"
var age:Int = 20
init() {
}
init(name:String, age:Int) {
self.name = name
self.age = age
}
}
现在,如果我们创建一个Person类的实例并将其标记为常量,它不会影响我们存储的变量属性,因为我们仍然可以改变它们的值,如下所示。
var person = Person()
person.name = "John"
person.age = 23
debugPrint(person.name + " " + person.age.description)
这将在控制台上打印以下输出。
"John 23"
然而,对于结构体类型来说情况并非如此,因为结构体包含值类型。如果我们将一个结构体标记为常量,尽管属性是可变的,但我们无法更改它的所有存储属性。考虑以下示例。
struct Person{
var name:String = "Anonymous"
var age:Int = 20
}
let person = Person()
person.name = "John" // will give compiler error as "Cannot assign to property: 'person' is a 'let' constant"
person.age = 23
debugPrint(person.name + " " + person.age.description)
惰性存储属性
惰性存储属性在系统中直到第一次使用它之前不占用内存。换句话说,惰性存储属性的值直到第一次访问它之前不会被计算出来。我们可以在Swift中使用”lazy”关键字将一个属性定义为惰性。然而,我们不能将惰性存储属性定义为常量,因为它的值可能在声明时无法检索到。
惰性属性在以下情况下非常有用:属性的值取决于外部因素,并且需要在这些值知道之后计算。如果属性在初始化时需要进行复杂的设置,并且在第一次访问值之前不需要使用,我们也可以使用惰性属性。
考虑以下示例,其中定义了类Employee和Department之间的关系。在这里,类Department包含一个类型为Employee的惰性存储属性,表示当与Employee相关联时将创建Employee属性。
import Foundation
class Employee{
var name:String
var id:Int
var salary:Double
var department:Department?
init(name:String,id:Int,salary:Double) {
self.name = name
self.id = id
self.salary = salary
}
}
class Department{
var name:String = "IT"
lazy var employees = Array()
}
var dep = Department()
dep.name = "CS"
var emp = Employee(name: "John", id: 102, salary: 25000.0)
emp.department = dep
dep.employees.append(emp) //the employee property will be created now
计算属性
我们可以将计算属性定义为类、结构体和枚举类型的实例的一部分。与存储属性不同,计算属性不存储值。相反,它们提供了getter和可选的setter来间接地检索和设置其他属性和值。
示例1
考虑下面的示例,定义了一个名为Person的类。Person有一个姓名、年龄和给世界的特殊信息。姓名和年龄是存储属性,而信息是一个计算属性,其值基于Person的姓名和年龄确定。
class Person{
var name:String
var age:Int
var festival:String
var message: String{
return "Hi I am \(name), and I am \(age) years old. I wish you all a very Happy \(festival)"
}
init(name:String,age:Int,festival:String) {
self.name = name
self.age = age
self.festival = festival
}
}
Person 类中包含了诸如名字、年龄和希望的节日之类的存储属性。然而,消息是通过计算得出的。因此,它是一个计算属性,其值基于一个人的名字、年龄和希望的节日来确定将要传递的消息。
var person = Person(name: "John", age: 23, festival: "Diwali")
debugPrint(person.message)
如果我们打印这个人的消息,下面的消息将被打印到控制台。
"Hi I am John, and I am 23 years old. I wish you all a very Happy Diwali"
示例2:
考虑以下示例,其中Circle类包含两个属性。 radius是存储属性,而area则是根据圆的半径计算出来的。
class Circle{
var radius:Double
var area:Double{
get{
return 3.14 * radius * radius
}
}
init(radius:Double) {
self.radius = radius
}
}
var circle = Circle(radius: 100)
debugPrint("Area of circle is \(circle.area)")
它在控制台上打印以下内容。
"Area of circle is 31400.0"
示例 3:
让我们扩展示例2中定义的Circle类,并计算给定圆的面积的半径。
class Circle{
var radius:Double
var area:Double{
get{
return 3.14 * radius * radius
}
set(newValue){
radius = sqrt(newValue/3.14)
}
}
init(radius:Double) {
self.radius = radius
}
}
如果我们为圆的面积分配某个值,它将根据面积计算半径,并将其分配给存储属性radius。
var circle = Circle(radius: 100)
circle.area = 31400
debugPrint("Radius of Circle is \(circle.radius)")
根据修改后的面积,将打印圆的新半径,如下所示。
"Radius of Circle is 316.2287731100305"