IOS開發語言Swift入門連載---屬性

來源:互聯網
上載者:User

標籤:ios   變數   屬性   

IOS開發語言Swift入門連載—屬性

 屬性將值跟特定的類、結構或枚舉關聯。儲存屬性儲存區常量或變數作為執行個體的一部分,計算屬性計算(而不是儲存)一個值。計算屬性可以用於類、結構體和枚舉裡,儲存屬性只能用於類和結構體。
  儲存屬性和計算屬性通常用於特定類型的執行個體,但是,屬性也可以直接用於類型本身,這種屬性稱為類型屬性。
  另外,還可以定義屬性監視器來監控屬性值的變化,以此來觸發一個自訂的操作。屬性監視器可以添加到自己寫的儲存屬性上,也可以添加到從父類繼承的屬性上。
  

儲存屬性

  簡單來說,一個儲存屬性就是儲存在特定類或結構體的執行個體裡的一個常量或變數,儲存屬性可以是變數儲存屬性(用關鍵字var 定義),也可以是常量儲存屬性(用關鍵字let 定義)。
  可以在定義儲存屬性的時候指定預設值,請參考構造過程一章的預設屬性值一節。也可以在構造過程中設定或修改儲存屬性的值,甚至修改常量儲存屬性的值,請參考構造過程一章的在初始化階段修改常量儲存屬性一節。
  下面的例子定義了一個名為FixedLengthRange 的結構體,它描述了一個在建立後無法修改範圍寬度的區間:

struct FixedLengthRange {    var firstValue: Int    let length: Int}var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)// 該區間表示整數0,1,2rangeOfThreeItems.firstValue = 6// 該區間現在表示整數6,7,8

  FixedLengthRange的執行個體包含一個名為firstValue 的變數儲存屬性和一個名為length 的常量儲存屬性。在上面的例子中,length 在建立執行個體的時候被賦值,因為它是一個常量儲存屬性,所以之後無法修改它的值。  常量和儲存屬性  
如果建立了一個結構體的執行個體並賦值給一個常量,則無法修改執行個體的任何屬性,即使定義了變數儲存屬性:

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)// 該區間表示整數0,1,2,3rangeOfFourItems.firstValue = 6// 儘管 firstValue 是個變數屬性,這裡還是會報錯

  因為rangeOfFourItems 聲明成了常量(用let 關鍵字),即使firstValue 是一個變數屬性,也無法再修改它了。  
這種行為是由於結構體(struct)屬於實值型別。當實值型別的執行個體被聲明為常量的時候,它的所有屬性也就成了常量。  
屬於參考型別的類(class)則不一樣,把一個參考型別的執行個體賦給一個常量後,仍然可以修改執行個體的變數屬性。  

延遲儲存屬性

 
延遲儲存屬性是指當第一次被調用的時候才會計算其初始值的屬性。在屬性聲明前使用@lazy來標示一個延遲儲存屬性。  
注意:  
必須將延遲儲存屬性聲明成變數(使用var關鍵字),因為屬性的值在執行個體構造完成之前可能無法得到。而常量屬性在構造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。  
延遲屬性很有用,當屬性的值依賴於在執行個體的構造過程結束前無法知道具體值的外部因素時,或者當屬性的值需要複雜或大量計算時,可以只在需要的時候來計算它。  
下面的例子使用了延遲儲存屬性來避免複雜類的不必要的初始化。例子中定義了DataImporter 和DataManager 兩個類,下面是部分代碼:

class DataImporter {    /*    DataImporter 是一個將外部檔案中的資料匯入的類。    這個類的初始化會消耗不少時間。    */    var fileName = "data.txt"    // 這是提供資料匯入功能}class DataManager {    @lazy var importer = DataImporter()    var data = String[]()    // 這是提供資料管理功能}let manager = DataManager()manager.data += "Some data"manager.data += "Some more data"// DataImporter 執行個體的 importer 屬性還沒有被建立

  DataManager 類包含一個名為data 的儲存屬性,初始值是一個空的字串(String )數組。雖然沒有寫出全部代碼,DataManager 類的目的是管理和提供對這個字串數組的訪問。  
DataManager 的一個功能是從檔案匯入資料,該功能由DataImporter 類提供,DataImporter 需要消耗不少時間完成初始化:因為它的執行個體在初始化時可能要開啟檔案,還要讀取檔案內容到記憶體。  
DataManager 也可能不從檔案中匯入資料。所以當DataManager 的執行個體被建立時,沒必要建立一個DataManager 的執行個體,更明智的是當用到DataImporter 的時候才去建立它。  
由於使用了@lazy ,importer 屬性只有在第一次被訪問的時候才被建立。比如訪問它的屬性fileName 時:

println(manager.importer.fileName)// DataImporter 執行個體的 importer 屬性現在被建立了// 輸出 "data.txt”
儲存屬性和執行個體變數

  
如果您有過 Objective-C 經驗,應該知道有兩種方式在類執行個體儲存值和引用。對於屬性來說,也可以使用執行個體變數作為屬性值的後端儲存。  
程式設計語言中把這些理論統一用屬性來實現。Swift 中的屬性沒有對應的執行個體變數,屬性的後端儲存也無法直接存取。這就避免了不同情境下訪問方式的困擾,同時也將屬性的定義簡化成一個語句。 一個類型中屬性的全部資訊——包括命名、類型和記憶體管理特徵——都在唯一一個地方(類型定義中)定義。  
計算屬性  
除儲存屬性外,類、結構體和枚舉可以定義計算屬性,計算屬性不直接儲存值,而是提供一個 getter 來擷取值,一個可選的 setter 來間接設定其他屬性或變數的值。

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)    }    }}var square = Rect(origin: Point(x: 0.0, y: 0.0),    size: Size(width: 10.0, height: 10.0))let initialSquareCenter = square.centersquare.center = Point(x: 15.0, y: 15.0)println("square.origin is now at (\(square.origin.x), \(square.origin.y))")// 輸出 "square.origin is now at (10.0, 10.0)”

  這個例子定義了 3 個幾何形狀的結構體:  
Point 封裝了一個(x, y) 的座標  
Size 封裝了一個width 和height  
Rect 表示一個有原點和尺寸的矩形  
Rect 也提供了一個名為center 的計算屬性。一個矩形的中心點可以從原點和尺寸來算出,所以不需要將它以顯式聲明的Point 來儲存。Rect 的計算屬性center 提供了自訂的 getter 和 setter 來擷取和設定矩形的中心點,就像它有一個儲存屬性一樣。  
例子中接下來建立了一個名為square 的Rect 執行個體,初始值原點是(0, 0) ,寬度高度都是10 。藍色正方形。  
square 的center 屬性可以通過點運算子(square.center)來訪問,這會調用 getter 來擷取屬性的值。跟直接返回已經存在的值不同,getter 實際上通過計算然後返回一個新的Point來表示square 的中心點。如代碼所示,它正確返回了中心點(5, 5) 。  
center 屬性之後被設定了一個新的值(15, 15) ,表示向右上方移動正方形到橙色正方形的位置。設定屬性center 的值會調用 setter 來修改屬性origin 的x 和y 的值,從而實現移動正方形到新的位置。

便捷 setter 聲明

  如果計算屬性的 setter 沒有定義表示新值的參數名,則可以使用預設名稱newValue 。下面是使用了便捷 setter 聲明的Rect 結構體代碼:

struct AlternativeRect {    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 {        origin.x = newValue.x - (size.width / 2)        origin.y = newValue.y - (size.height / 2)    }    }}
唯讀計算屬性

  只有 getter 沒有 setter 的計算屬性就是唯讀計算屬性。唯讀計算屬性總是返回一個值,可以通過點運算子訪問,但不能設定新的值。
  注意:
  必須使用var 關鍵字定義計算屬性,包括唯讀計算屬性,因為他們的值不是固定的。var 關鍵字只用來聲明常量屬性,表示初始化後再也無法修改的值。
  注意:
  必須使用var 關鍵字定義計算屬性,包括唯讀計算屬性,因為它們的值不是固定的。var 關鍵字只用來聲明常量屬性,表示初始化後再也無法修改的值。

  唯讀計算屬性的聲明可以去掉get 關鍵字和花括弧:

struct Cuboid {    var width = 0.0, height = 0.0, depth = 0.0    var volume: Double {    return width * height * depth    }}let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)println("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")// 輸出 "the volume of fourByFiveByTwo is 40.0"

  這個例子定義了一個名為Cuboid 的結構體,表示三維空間的立方體,包含width 、height 和depth 屬性,還有一個名為volume 的唯讀計算屬性用來返回立方體的體積。設定volume 的值毫無意義,因為通過width 、 height 和depth 就能算出volume 。然而,Cuboid 提供一個唯讀計算屬性來讓外部使用者直接擷取體積是很有用的。
  

屬性監視器

  屬性監視器監控和響應屬性值的變化,每次屬性被設定值的時候都會調用屬性監視器,甚至新的值和現在的值相同的時候也不例外。
  可以為除了延遲儲存屬性之外的其他儲存屬性添加屬性監視器,也可以通過重載屬性的方式為繼承的屬性(包括儲存屬性和計算屬性)添加屬性監視器。屬性重載請參考繼承一章的重載。
  注意:
  不需要為無法重載的計算屬性添加屬性監視器,因為可以通過 setter 直接監控和響應值的變化。
  可以為屬性添加如下的一個或全部監視器:
  willSet 在設定新的值之前調用
  didSet 在新的值被設定之後立即調用
  willSet 監視器會將新的屬性值作為固定參數傳入,在willSet 的實現代碼中可以為這個參數指定一個名稱,如果不指定則參數仍然可用,這時使用預設名稱newValue 表示。
  類似地,didSet 監視器會將舊的屬性值作為參數傳入,可以為該參數命名或者使用預設參數名oldValue 。

  注意:
   willSet 和didSet 監視器在屬性初始化過程中不會被調用,他們只會當屬性的值在初始化之外的地方被設定時被調用。
  注意:
   willSet 和didSet 監視器在屬性初始化過程中不會被調用,它們只會當屬性的值在初始化之外的地方被設定時被調用。
  這裡是一個willSet 和didSet 的實際例子,其中定義了一個名為StepCounter 的類,用來統計當人步行時的總步數,可以跟計步器或其他日常鍛煉的統計裝置的輸入資料配合使用。

class StepCounter {    var totalSteps: Int = 0 {    willSet(newTotalSteps) {        println("About to set totalSteps to \(newTotalSteps)")    }    didSet {        if totalSteps > oldValue  {            println("Added \(totalSteps - oldValue) steps")        }    }    }}let stepCounter = StepCounter()stepCounter.totalSteps = 200// About to set totalSteps to 200// Added 200 stepsstepCounter.totalSteps = 360// About to set totalSteps to 360// Added 160 stepsstepCounter.totalSteps = 896// About to set totalSteps to 896// Added 536 steps

  StepCounter類定義了一個Int 類型的屬性totalSteps ,它是一個儲存屬性,包含willSet 和didSet 監視器。
  當totalSteps 設定新值的時候,它的willSet 和didSet 監視器都會被調用,甚至當新的值和現在的值完全相同也會調用。
  例子中的willSet 監視器將表示新值的參數自訂為newTotalSteps ,這個監視器只是簡單的將新的值輸出。
  didSet監視器在totalSteps 的值改變後被調用,它把新的值和舊的值進行對比,如果總的步數增加了,就輸出一個訊息表示增加了多少步。didSet 沒有提供自訂名稱,所以預設值oldValue 表示舊值的參數名。
  注意:
  如果在didSet 監視器裡為屬性賦值,這個值會替換監視器之前設定的值。
  

全域變數和局部變數

  計算屬性和屬性監視器所描述的模式也可以用於全域變數和局部變數,全域變數是在函數、方法、閉包或任何類型之外定義的變數,局部變數是在函數、方法或閉包內部定義的變數。
  前面章節提到的全域或局部變數都屬於儲存型變數,跟儲存屬性類似,它提供特定類型的儲存空間,並允許讀取和寫入。
  另外,在全域或局部範圍都可以定義計算型變數和為儲存型變數定義監視器,計算型變數跟計算屬性一樣,返回一個計算的值而不是儲存值,聲明格式也完全一樣。
  注意:
  全域的常量或變數都是延遲計算的,跟延遲儲存屬性相似,不同的地方在於,全域的常量或變數不需要標記@lazy 特性。
  局部範圍的常量或變數不會延遲計算。
  

類型屬性

  執行個體的屬性屬於一個特定類型執行個體,每次類型執行個體化後都擁有自己的一套屬性值,執行個體之間的屬性相互獨立。
  也可以為類型本身定義屬性,不管類型有多少個執行個體,這些屬性都只有唯一一份。這種屬性就是類型屬性。
  類型屬性用於定義特定類型所有執行個體共用的資料,比如所有執行個體都能用的一個常量(就像 C 語言中的靜態常量),或者所有執行個體都能訪問的一個變數(就像 C 語言中的靜態變數)。
  對於實值型別(指結構體和枚舉)可以定義儲存型和計算型類型屬性,對於類(class)則只能定義計算型類型屬性。
  實值型別的儲存型類型屬性可以是變數或常量,計算型類型屬性跟執行個體的計算屬性一樣定義成變數屬性。
  注意:
  跟執行個體的儲存屬性不同,必須給儲存型類型屬性指定預設值,因為類型本身無法在初始化過程中使用構造器給類型屬性賦值。
 

類型屬性文法

  在 C 或 Objective-C 中,靜態常量和靜態變數的定義是通過特定類型加上global 關鍵字。在 Swift 程式設計語言中,類型屬性是作為類型定義的一部分寫在類型最外層的花括弧內,因此它的作用範圍也就在類型支援的範圍內。
  使用關鍵字static 來定義實值型別的類型屬性,關鍵字class 來為類(class)定義類型屬性。下面的例子示範了儲存型和計算型類型屬性的文法:

struct SomeStructure {    static var storedTypeProperty = "Some value."    static var computedTypeProperty: Int {    // 這裡返回一個 Int 值    }}enum SomeEnumeration {    static var storedTypeProperty = "Some value."    static var computedTypeProperty: Int {    // 這裡返回一個 Int 值    }}class SomeClass {    class var computedTypeProperty: Int {    // 這裡返回一個 Int 值    }}

  注意:
  例子中的計算型類型屬性是唯讀,但也可以定義可讀可寫的計算型類型屬性,跟執行個體計算屬性的文法類似。
  擷取和設定類型屬性的值
  跟執行個體的屬性一樣,類型屬性的訪問也是通過點運算子來進行,但是,類型屬性是通過類型本身來擷取和設定,而不是通過執行個體。比如:

println(SomeClass.computedTypeProperty)// 輸出 "42"println(SomeStructure.storedTypeProperty)// 輸出 "Some value."SomeStructure.storedTypeProperty = "Another value."println(SomeStructure.storedTypeProperty)// 輸出 "Another value.”

  下面的例子定義了一個結構體,使用兩個儲存型類型屬性來表示多個聲道的聲音電平值,每個聲道有一個 0 到 10 之間的整數表示聲音電平值。
  後面的圖表展示了如何聯合使用兩個聲道來表示一個立體聲的聲音電平值。當聲道的電平值是 0,沒有一個燈會亮;當聲道的電平值是 10,所有燈點亮。本圖中,左聲道的電平是 9,右聲道的電平是 7。

  上面所描述的聲道模型使用AudioChannel 結構體來表示:

struct AudioChannel {    static let thresholdLevel = 10    static var maxInputLevelForAllChannels = 0    var currentLevel: Int = 0 {    didSet {        if currentLevel > AudioChannel.thresholdLevel {            // 將新電平值設定為閥值            currentLevel = AudioChannel.thresholdLevel        }        if currentLevel > AudioChannel.maxInputLevelForAllChannels {            // 儲存當前電平值作為新的最大輸入電平            AudioChannel.maxInputLevelForAllChannels = currentLevel        }    }    }}

  結構AudioChannel 定義了 2 個儲存型類型屬性來實現上述功能。第一個是thresholdLevel ,表示聲音電平的最大上限閾值,它是一個取值為 10 的常量,對所有執行個體都可見,如果聲音電平高於 10,則取最大上限值 10(見後面描述)。
  第二個類型屬性是變數儲存型屬性maxInputLevelForAllChannels ,它用來表示所有AudioChannel 執行個體的電平值的最大值,初始值是 0。
  也定義了一個名為currentLevel 的執行個體儲存屬性,表示當前聲道現在的電平值,取值為 0 到 10。
  屬性currentLevel 包含didSet 屬性監視器來檢查每次新設定後的屬性值,有如下兩個檢查:
  如果currentLevel 的新值大於允許的閾值thresholdLevel ,屬性監視器將currentLevel 的值限定為閾值thresholdLevel 。
  如果修正後的currentLevel 值大於任何之前任意 AudioChannel 執行個體中的值,屬性監視器將新值儲存在靜態屬性maxInputLevelForAllChannels 中。
  注意:
  在第一個檢查過程中,didSet 屬性監視器將currentLevel 設定成了不同的值,但這時不會再次調用屬性監視器。
  可以使用結構體AudioChannel 來建立表示立體聲系統的兩個聲道leftChannel 和rightChannel :

var leftChannel = AudioChannel()var rightChannel = AudioChannel()

  如果將左聲道的電平設定成 7,類型屬性maxInputLevelForAllChannels 也會更新成 7:

leftChannel.currentLevel = 7println(leftChannel.currentLevel)// 輸出 "7"println(AudioChannel.maxInputLevelForAllChannels)// 輸出 "7"

  如果試圖將右聲道的電平設定成 11,則會將右聲道的currentLevel 修正到最大值 10,同時maxInputLevelForAllChannels 的值也會更新到 10:

rightChannel.currentLevel = 11println(rightChannel.currentLevel)// 輸出 "10"println(AudioChannel.maxInputLevelForAllChannels)// 輸出 "10"

IOS開發語言Swift入門連載---屬性

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.