属性与下标

226 阅读9分钟

存储属性

存储属性可以存储数据,分为常量属性(用关键字let定义)和变量属性(用关键字var定义)。存储属性适用于类和结构体两种Swift面向对象类型。

存储属性概念

class Employee {
    let no : Int = 0
    var name : String = ""
    var job : String?
    var salary : Double = 0
    var dept : Department?
}

struct Department {
    let no : Int = 0
    var name : String = ""
}

var emp = Employee()

emp.no = 100                //编译错误


let dept = Department()
dept.name = 30              //编译错误

let emp1 = Employee()
emp1.name = "Tony"

实例通过点(.)运算符调用属性。代码emp.no = 100修改常量属性,程序发生编译错误,dept.name = 30也会发生编译错误,因为实例dept本身是常量,即便它的属性name是变量属性,也不能修改。但是代码emp1.name = "Tony"可以编译通过,emp1实例也是常量,name是变量属性。因为emp1是类实例,是引用类型,dept是结构体实例,是值类型。引用类型相当于指针,其常量也可以修改,但值类型的常量是不能修改的。

延迟存储属性

print("--延迟存储属性--")

class Employee {
    var no : Int = 0
    var name : String = ""
    var job : String?
    var salary : Double = 0
    lazy var dept : Department = Department()
}

struct Department {
    let no : Int = 0
    var name : String = ""
}

let emp = Employee()

在dept属性前面添加了关键字lazy声明,这样dept属性就是延时加载。延时加载,顾名思义,就是dept属性只有在第一次访问它的时候才加载,如果永远不访问,它就不会创建,这样就可以减少内存占用。

属性观察者

为了监听属性的变化,可以使用Swift提供的以下属性观察者。

  • willSet在设置新的值之前调用
  • didSet在新值被设置之后马上调用

可以根据需要,使用部分或全部的属性观察者。它们不能应用于延迟存储属性,但能够应用于一般的存储属性和计算属性。


计算属性

计算属性本身不存储数据,而是从其他存储属性中计算得到数据。与存储数据不同,类、结构体和枚举都可以定义计算属性。

计算属性概念 计算属性提供了一个getter(取值访问器)来获取值,以及一个可选的setter(设置访问器)来间接设置其他属性或变量的值。计算属性的语法格式如下:

面向对象类型 类型名 {
存储属性
......
var 计算属性名 : 属性数据类型 {
get {
return 计算后属性值
}
set (新属性值) {
......
}
}
}

“面向对象类型”包括类、结构体和枚举3种。"存储属性"表示有很多存储属性。

示例:

import UIKit

class Employee {
    var no : Int = 0
    var firstName : String = "Tony"
    var lastName : String = "Guan"
    var job : String?
    var salary : Double = 0
    lazy var dept : Department = Department()
    
    var fullName : String {
        get {
            return firstName + "." + lastName
        }
//        set (newFullName) {
//            var name = newFullName.componentsSeparatedByString()
//            firstName = name[0]
//            lastName = name[1]
//        }
        set {
            var name = newValue.componentsSeparatedByString(".")
            firstName = name[0]
            lastName = name[1]
        }
    }
    
}

struct Department {
    let no : Int = 0
    var name : String = ""
}

var emp = Employee()
print(emp.fullName)

emp.fullName = "Tom.Guan"
print(emp.fullName)

引入UIKit框架,set (newFullName)可以省略如下形式,使用Swift默认名称newValue替换newFullName。

set {
            var name = newValue.componentsSeparatedByString(".")
            firstName = name[0]
            lastName = name[1]
        }

newValue.componentsSeparatedByString(".")使用String的字符串分割方法componentsSeparatedByString,指定点为字符串的分割符号,分割方法返回的是String数组。

只读计算属性

计算属性可以只有getter访问器,没有setter访问器,这就是只读计算属性。指定计算属性不仅不用写setter访问器,而且get{}代码也可以省略。修改示例为只读计算属性,代码如下:

import UIKit

class Employee {
    var no : Int = 0
    var firstName : String = "Tony"
    var lastName : String = "Guan"
    var job : String?
    var salary : Double = 0
    lazy var dept : Department = Department()
    
    var fullName : String {
        return firstName + "." + lastName
    }
}


struct Department {
    let no : Int = 0
    var name : String = ""
}

var emp = Employee()
print(emp.fullName)

只读计算属性不能够赋值。

结构体和枚举中的计算属性

示例代码:

import UIKit

struct Department {
    let no : Int = 0
    var name : String = "SALES"
    
    var fullName : String {
        return "Swift." + name + ".D"
    }
}

var dept = Department()
print(dept.fullName)


enum WeekDays : String {
    case Monday     = "Mon."
    case Tuesday    = "Tue."
    case Wednesday  = "Wed."
    case Thursday   = "Thu."
    case Friday     = "Fri."
    
    var message : String {
        return "Today is " + self.rawValue
    }
}

var day = WeekDays.Monday
print(day.message)

属性观察者

为了监听属性的变化,Swift提供了属性观察者。属性观察者能够监听存储属性的变化,即便变化前后的值相同,它们也能监听到。但它们不能监听延迟存储属性和常量存储属性的变化。

Swift中的属性观察者主要有以下两个。

  • willSet:观察者在修改之前调用
  • didSet:观察者在修改之后立刻调用

属性观察者的语法格式如下:

面向对象类型 类型名 {
......
var 存储属性 : 属性数据类型 = 初始化值 {
willSet(新值) {
......
}
didSet(旧值) {
......
}
}
} 属性观察者的语法格式比计算属性还要混乱。“面向对象类型”包括类和结构体,不包括枚举,因为枚举不支持存储属性。

class Employee {
    var no : Int = 0
    var name : String = "Tony" {
        willSet (newNameValue) {
            print("员工name新值:\(newNameValue)")
        }
        didSet (oldNameValue) {
            print("员工name旧值:\(oldNameValue)")
        }
    }
    var job : String?
    var salary : Double = 0
    var dept : Department?
}

struct Department {
    var no : Int = 10 {
        willSet {
            print("员工编号新值:\(newValue)")
        }
        didSet {
            print("员工编号旧值:\(oldValue)")
        }
    }
    var name : String = "RESEARCH"
}

var emp = Employee()
emp.no = 100
emp.name = "Smith"

var dept = Department()
dept.no = 30

静态属性

一个类的设计:有一个Account(银行账户)类,假设它有3个属性:amount(账户金额)、interestRate(利率)和owner(账户名)。在这3个属性中,amount和owner会因人而异,不同的账户这些内容是不同的,而所有账户的interestRate都是相同的。

amount和owner属性与账户个体有关,称为实例属性。interestRate属性与个体无关,或者说是所有账户个体共享的,这种属性称为静态属性或类型属性。

3种面向对象类型(结构体、枚举和类)都可以定义静态属性,它们的语法格式分别如下所示:

struct 结构体名 {
static var(或let) 存储属性 = "xxx"
......
static var 计算属性名 : 属性数据类型 {
get {
return 计算后属性值
}
set (新属性值) {
......
}
}
}

enum 枚举名 {
static var (或let) 存储属性 = "xxx"
......
static var 计算属性名 : 属性数据类型 {
get {
return 计算后属性值
}
set (新属性值) {
......
}
}
}

class 类名 {
......
class var 计算属性名 : 属性数据类型 {
get {
return 计算后属性值
}
set (新属性值) {
......
}
}
} 结构体中可以定义静态存储属性和计算属性;定义静态存储属性,声明关键字是static,这个属性可以是变量属性也可以是常量属性;定义静态计算属性,声明使用的关键字是static,计算属性不能为常量。结构体静态计算属性也可以是只读的,语法如下:

static var 计算属性名 : 属性数据类型 {
return 计算后属性值
} 定义枚举,枚举中不可以定义实例存储属性,但可以定义静态存储属性,也可以定义静态计算属性。定义枚举静态属性与定义结构体静态属性的语法完全一样。

定义类,类中可以定义实例存储属性,但不可以定义静态存储属性。类中可以定义静态计算属性。声明使用的关键字是class,这与结构体和枚举的声明不同。

归纳:

面向对象类型 实例存储属性 静态存储属性 实例计算属性 静态计算属性
支持 不支持 支持 支持
结构体 支持 支持 支持 支持
枚举 不支持 支持 支持 支持

提示:在静态计算属性中不能访问实例属性(包括存储属性和计算属性),但可以访问其他静态属性。在实例计算属性中能访问实例属性,也能访问静态属性。

结构体静态属性

struct Account {
    var amount : Double = 0.0       //账户金额
    var owner : String = ""         //账户名
    
    static var interestRate : Double = 0.668       //利率
    
    static var staticProp : Double {
        return interestRate * 1_000_000
    }
    
    var instanceProp : Double {
        return Account.interestRate * amount
    }
}

//访问静态属性
print(Account.staticProp)

var myAccount = Account()
//访问实例属性
myAccount.amount = 1_000_000

//访问实例属性
print(myAccount.instanceProp)

上述代码定义了Account结构体,static var interestRate : Double = 0.668定义了静态存储属性interestRate,代码 static var staticProp : Double { return interestRate * 1_000_000 }定义了静态计算属性staticProp,在其属性体中可以访问interestRate等静态属性。代码var instanceProp : Double { return Account.interestRate * amount }定义了实例计算属性instanceProp,在其属性体中能访问静态属性interestRate,访问方式为“类型名.静态属性”。访问实例属性访问方式是“实例.实例属性”。

枚举静态属性

enum Account {
    case 中国银行
    case 中国工商银行
    case 中国建设银行
    case 中国农业银行
    
    static var interestRate : Double = 0.668
    
    static var staticProp : Double {
        return interestRate * 1_000_000
    }
    
    var instanceProp : Double {
        switch (self) {
        case 中国银行:
            Account.interestRate = 0.667
        case 中国工商银行:
            Account.interestRate = 0.669
        case 中国建设银行:
            Account.interestRate = 0.666
        case .中国农业银行:
            Account.interestRate = 0.668
        }
        return Account.interestRate * 1_000_000
    }
    
}

//访问静态属性
print(Account.staticProp)

var myAccount = Account.中国工商银行
//访问实例属性
print(myAccount.instanceProp)

类静态属性

class Account {
    var amount : Double = 0.0   //账户金额
    var owner : String = ""     //账户名
    
    var interestRate : Double = 0.668       //利率
    
    class var staticProp : Double {
        return 0.668 * 1_000_000
    }
    
    var instanceProp : Double {
        return self.interestRate * self.amount
    }
    
}

//访问静态属性
print(Account.staticProp)

var myAccount = Account()

//访问实例属性
myAccount.amount = 1_000_000

//访问静态属性
print(myAccount.instanceProp)

注意在类中不能定义静态存储属性。代码class var staticProp : Double 定义了静态计算属性staticProp,关键字是class。self指代当前实例本身。


使用下标

在访问数组和字典的时候,可以采用下标访问。其中数组的下标是整数类型索引,字典的下标是它的"键"。

下标概念 在Swift中,可以定义一些集合类型,它们可能会有一些集合类型的存储属性,这些属性中的元素可以通过下标访问。Swift中的下标相当于Java中的索引属性和C#中的索引器。

下标访问的语法格式如下:

面向对象类型 类型名 {
其他属性
......
subscript(参数: 参数数据类型) -> 返回值数据类型 {
get {
return 返回值
}
set (新属性值) {
......
}
}
}

“面向对象类型”包括类、结构体和枚举3种。下标采用subscript关键字声明。下标也有类似于计算属性的getter和setter访问器。

示例: 二维数组 在Swift中没有提供二维数组,只有一维数组Array。可以自定义一个二维数组类型,然后通过两个下标参数访问它的元素,形式上类似于C语言的二维数组。

采用下标的二维数组示例代码如下:

struct DoubleDimensionalArray {
    let rows : Int, columns : Int
    var grid : [Int]
    
    init(rows: Int, columns: Int) {
        self.rows = rows
        self.columns = columns
        grid = Array(count:rows * columns, repeatedValue:0)
    }
    
    subscript(row: Int, col: Int) -> Int {
        get {
            return grid[(row * columns) + col]
        }
        set (newValue1) {
            grid[(row * columns) + col] = newValue1
        }
    }
    
    
}

let COL_NUM = 10
let ROW_NUM = 10

var ary2 = DoubleDimensionalArray(rows: ROW_NUM, columns: COL_NUM)

for i in 0 ..< ROW_NUM {
    for j in 0 ..< COL_NUM {
        ary2[i,j] = i * j
    }
}

for i in 0 ..< ROW_NUM {
    for j in 0 ..< COL_NUM {
        print("\t \(ary2[i,j])")
    }
    print("\n")
}