關於Swift中Properties的一些理解

godiscoder發表於2019-02-26

Properties

1、計算屬性(Computed properties)由類、結構體和列舉提供,儲存屬性(Stored properties)只能由類和結構體提供(儲存屬性是儲存例項的一個常量或者變數的值,而計算屬性則是計算一個值,而不是儲存)。

2、當一個值型別(value type)的例項被宣告為常量時,它的所有屬性也會自動變為常量而不可更改。如果你宣告一個引用型別(reference type)的例項,你仍然可以修改它的可變屬性。
Example:

//結構體為值型別
struct FixedLengthRange {
    var firstValue = 3
}
//類為引用型別
class Student {
    var age = 3
}
let john = Student()
john.age = 4
let range = FixedLengthRange()
range.firstValue = 3//這句程式碼報錯複製程式碼

3、Lazy Stored Properties

lazy property:當該屬性第一次被呼叫時,才會計算它的初始值。用lazy標識。

作用:

  • 當一個屬性的初始值是取決於一直到例項初始化完成才知道值的外部元素的時候。
  • 當屬性的初始值需要複雜或者大量的計算,可以在需要它的時候再初始化它。

注意事項:

  • 你必須用var關鍵字將lazy property宣告為變數, 因為它的初始值可能在例項初始化完成才會得到。而常量屬性必須在初始化完成之前就有值,所以常量不能用lazy宣告。
  • 如果一個被標記為lazy的屬性在初始化完成之前被多個執行緒同時訪問,則不能保證該屬性只被初始化一次。

Example:

class DataImporter {
    var fileName = "data.txt"
}

class DataManager {
    lazy var importer = DataImporter()
    var data = [String]()
    // the DataManager class would provide data management functionality here
}

let manager = DataManager()
manager.data.append("Some data")
manager.data.append("Some more data")
//此時雖然manager的data屬性被初始化,但是因為沒有用到fileName屬性,所以它還沒有被初始化
print(manager.fileName)//fileName從這一句才被初始化,賦值複製程式碼

4、計算屬性(computed property)

  • 如果一個計算屬性(computed property)只有getter方法而沒有setter方法,那該屬性就為只讀計算屬性。一個只讀的計算屬性總是返回一個值,它可以通過點語法來訪問,但是它的值不能被修改。
  • 你必須用var關鍵字將計算屬性(包括只讀計算屬性)宣告為變數,因為它們的值不是固定的。let關鍵字只能適用於常量屬性,用來說明這些屬性一旦被初始化就不會被改變值。

5、屬性觀察者(property observers)

  • 你可以給你定義的任意儲存屬性新增屬性觀察者,lazy儲存屬性除外。
  • willSet在值被儲存前呼叫。
  • didSet 在新值被儲存之後立刻呼叫。
  • 當一個屬性在子類中被設定時,父類的willSetdidSet也會被呼叫,子類的初始化方法在父類之後呼叫。

Example:

class StepCounter {
    var totalSteps: Int = 0 {
        willSet(newTotalSteps) {
            print("About to set totalSteps to (newTotalSteps)")
        }
        didSet {
            if totalSteps > oldValue  {
                print("Added (totalSteps - oldValue) steps")
            }
        }
    }
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps複製程式碼

6、全域性變數和區域性變數(global and local variable)

  • 全域性變數:定義在任何函式、方法、閉包或者任何型別之外的變數
  • 區域性變數:定義在函式、方法、閉包裡面的變數
  • 全域性變數或者常量總是被延遲計算,和延遲儲存屬性不同的是,它不需要lazy關鍵詞修飾。
  • 區域性常量或者變數永遠不會延遲計算。

7、型別屬性(type properties)

  • 型別屬性:為型別本身定義屬性,無論建立了多少個該型別的例項,這些屬性都只有唯一一份。這種屬性就是型別屬性.請注意它和例項屬性是不一樣的。例項屬性是例項的一種特殊的型別,當你建立每一個例項的時候它的屬性都是獨立的。

  • 比如下面的例子,每個Size的例項的屬性都是相對獨立的,你可以分別給它們賦值。但是Point則不能,因為它是型別屬性(由static關鍵字修飾),你只能通過(type)Point來訪問它。

Example:

struct Size {
    var width = 0.0
    var height = 0.0
}

struct Point {
   static var x = 0.0
   static var y = 0.0
}

var size1 = Size()
size1.width = 10
var size2 = Size()
size2.width = 20
print(size1,size2)
//Size(width: 10.0, height: 0.0) Size(width: 20.0, height: 0.0)
Point.x = 10
Point.y = 20
print(Point.x, Point.y)
//10.0 20.0複製程式碼
  • 你要通過static關鍵字來定義型別屬性。對於類的計算型別屬性,你可以使用class關鍵字來支援子類對父類的重寫。下面的例子是宣告的只讀計算屬性,你也可以定義為讀寫屬性,語法和計算屬性的定義語法一樣。

Example:

class SomeClass {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {
        return 27
    }
    class var overrideableComputedTypeProperty: Int {
        return 107
    }
}複製程式碼
  • 這是官方文件上型別屬性的一個例子:

程式碼解讀:thresholdLevel表示的最大閾值,maxInputLevelForAllChannels表示最大的輸入值。當你輸入的currentLevel大於thresholdLevel時,thresholdLevel會將本身的值賦給currentLevel(如第一個if語句)。如果currentLevel值大於maxInputLevelForAllChannelscurrentLevel會將本身的值賦給maxInputLevelForAllChannels(如第二個if語句)
Example:

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()

leftChannel.currentLevel = 7
print(leftChannel.currentLevel)
// Prints "7"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "7"

rightChannel.currentLevel = 11
print(rightChannel.currentLevel)
// Prints "10"
print(AudioChannel.maxInputLevelForAllChannels)
// Prints "10"複製程式碼

本文github地址

相關文章