# 属性

Swift中的属性分为`存储属性`和`计算属性`。

存储属性会将常量和变量存储为实例的一部分。

计算属性则是返回一个计算后的值，而非存储的值。

计算属性可以用于类，结构体和枚举；存储属性只能用于类和结构体。

## 1. 存储属性

`存储属性`定义在`class`和`struct`之中，是对象的数据成员的一部分，需要占据内存空间。

`class`和`struct`必须要提供`初始化所有存储属性`的构造器。`struct`会自动生成一个构造器；`class`则必须手动实现。

![](https://pic.existorlive.cn/%E6%88%AA%E5%B1%8F2020-11-30%20%E4%B8%8A%E5%8D%8812.51.40.png)

```swift

struct FixedLengthRangeStruct {
    var firstValue: Int           // 变量
    let length: Int               // 常量
}

// struct 会自动生成为每个存储属性初始化的构造器
let obj1 : FixedLengthRangeStruct = FixedLengthRangeStruct(firstValue:12,length:12)

class FixedLengthRangeClass {
    var firstValue: Int           // 变量
    let length: Int               // 常量   

    init(){
        firstValue = 13
        length = 13
    }
}

let obj2 = FixedLengthRangeClass()  


```

### 1.1 定义存储属性时提供初始值

当为常量属性提供初始值时，构造器中不可以再重新赋值。

当为变量属性提供初始值时，构造器中仍可以再重新赋值

```swift

struct FixedLengthRangeStruct {
    var firstValue: Int 
    let length: Int = 13
    var strArray : [String] = []
}

// struct 会自动生成为每个存储属性初始化的构造器
let obj1 : FixedLengthRangeStruct = FixedLengthRangeStruct(firstValue:12)

class FixedLengthRangeClass {
    var firstValue: Int           // 变量
    let length: Int               // 常量   
    var strArray : [String] = []

    init(){
        firstValue = 13
        length = 13
        // 定义时提供初始值的属性不需要在构造器中再提供初始值
    }
}

let obj2 = FixedLengthRangeClass()  


```

### 1.2 延时加载存储属性

`延时加载存储属性`是指当第一次被访问的时候才会计算其初始值的属性。

```swift
struct FixedLengthRangeStruct {
    lazy var firstValue: Int = Int(15)
    let length: Int = 13
}
```

> 延时加载属性必须声明为变量

> 延时加载属性必须提供初始化器

> 延迟加载属性不能保证线程安全

`延时加载存储属性`在对象初始化之后,才会初始化。因此不可以声明为常量。同时必须在声明时提供一个初始化器。

`延时加载存储属性`适用于一些需要较大开销才能够获得属性。因此将属性的初始化时机推迟到使用时。

### 1.3 常量结构体实例的存储属性

类或者结构体实例中的常量成员在初始化之后不可以再修改。

> 常量的结构体实例中的属性,无论是常量还是变量都不可以修改

> 常量的类实例中的变量属性可以修改

![](https://pic.existorlive.cn/%E6%88%AA%E5%B1%8F2020-11-30%20%E4%B8%8A%E5%8D%882.02.55.png)

```swift

// 结构体
struct FixedLengthRange {
    var firstValue: Int
    let length: Int
}

let obj1 : FixedLengthRange = FixedLengthRange(firstValue:12,length:13)
obj1.firstValue = 15    // 报错

// 类
class FixedLengthRange {
    var firstValue: Int
    let length: Int
    init(){
        firstValue = 15
        length = 13
    }
}

let obj2 : FixedLengthRange = FixedLengthRange()
obj2.firstValue = 15    // 可以重新赋值

```

## 2. 计算属性

`计算属性`不同于`存储属性`，不需要存储值，不会在对象实例中占据内存。通过提供setter，getter方法，来间接获取和设置其他属性或变量的值。

`类`，`结构体`以及`枚举`都可以定义`计算属性`。

计算属性必须使用`var`声明

```swift
struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var origin = Point()
    var size = 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)
        }
    }
}
```

### 2.1 简化Setter和Getter声明

```swift
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            // 单行表达式可以参略return 
            Point(x: origin.x + (size.width / 2), y: origin.y + (size.height / 2))
        }
        set{
            // setter可以不用提供新值的参数名，可以使用默认名称newValue
            origin.x = newValue.x - (size.width / 2)
            origin.y = newValue.y - (size.height / 2)
        }
    }
}

```

### 2.2 只读计算属性

只有 `getter` 没有 `setter` 的计算属性叫只读计算属性。

```swift

struct Cuboid {
    var width = 0.0, height = 0.0, depth = 0.0
    // 只读计算属性可以省略get{}
    var volume: Double {
        return width * height * depth
    }
}
```

## 3. 存储属性和计算属性

* 存储属性
  * 存储属性需要存储值，占据对象的内存
  * 存储属性可以定义为常量和变量
  * 只有类和结构体才能够定义存储属性
  * 存储属性需要提供初始化器或者在构造器中初始化
  *
* 计算属性
  * 计算属性不需要存储值，但是需要提供setter和getter方法
  * 计算属性只能使用`var`声明
  * 类，结构体和枚举都可以定义计算属性
  *
