Swift編程高級教程 變量與常量

常量與變量

常量和變量是某個特定類型的值的名字,如果在程序運行時值不能被修改的是一個常量,反之是一個變量。

一、常量和變量的聲明

Swift中的常量和變量在使用前必須先聲明。其中let關鍵字聲明常量,var關鍵字聲明變量:

//聲明一個名爲maximumNumberOfLoginAttempts的整型常量,並且值爲10let maximumNumberOfLoginAttempts = 10//聲明一個名爲currentLoginAttempt的整型變量,並且值爲0var currentLoginAttempt = 0

可以在同一行聲明多個變量,中間用逗號,隔開:

var x = 0.0, y = 0.0, z = 0.0

提示
如果在程序運行的時候值不需要發生改變,應該將它們聲明爲常量,否則聲明爲變量

變量的值可以進行修改:

var friendlyWelcome = "Hello!"friendlyWelcome = "Bonjour!" //friendlyWelcome的值發生改變

常量的值一旦設置後就不能在修改:

let languageName = "Swift"languageName = "Swift++" //編譯時出錯

類型說明

在Swift中聲明常量或者變量可以在後面用冒號:指定它們的數據類型。

//聲明一個String類型的變量,可以存放String類型的值var welcomeMessage: String

提示
實際應用中很少需要指定變量數據類型,Swift會根據所設置的值的類型進行推導。

命名規則

Swift中可以使用任意字符給常量和變量命名,包括Unicode編碼,比如中文、Emoji等:

let π = 3.14159let 你好 = "你好世界"let dog = "dogcow"

名字裏面不能包含數學運算符、箭頭、非法的Unicode字符以及不能識別的字符等,並且不能以數字開頭。同一個作用域的變量或者常量不能同名。

提示
如果想用關鍵字作爲變量的名字,要用(`)包裹起來。爲了方便理解,如果不是萬不得已,不應該使用關鍵字作爲變量的名字。

打印變量的值

println函數可以打印常量或者變量的值:

println(friendlyWelcome) //打印出“Bonjour!”

println是一個全局函數,輸出時自動在結尾添加換行。print函數與它的功能類似,只是不會在結尾添加換行符。printlnNSLog類似,可以用來輸出複雜的日誌信息。Swift使用字符串插值來輸出變量和常量。

println("The current value of friendlyWelcome is \(friendlyWelcome)")//打印“The current value of friendlyWelcome is Bonjour!”

註釋

註釋是用來幫助理解和記憶代碼功能的,並不會參與編譯。Swift有兩種註釋形式,單行註釋和多行註釋:

//這是單行註釋,用兩個斜線開頭,直到改行的結尾/*這是多行註釋,
可以橫跨很多行,
/*比C語言更加NB的是,*/它竟然還支持嵌套的註釋!*/

分號

Swift中語句結尾的分號;不是必須的,不過如果想要在同一行中寫多個語句,則需要使用;進行分隔。

let cat = "cat"; println(cat)//打印“cat”

二、屬性

屬性是依賴於某個特定的類、結構體或者枚舉類型的值。Swift有兩種屬性:存儲類型和計算類型。其中存儲類型可以作爲實例的一部分存放變量或者常量的值,而計算類型的屬性值是通過運算的來的。計算類型的屬性可以在類、結構體和枚舉類型中出現,但存儲類型只可能出現在類和結構體類型中。

屬性一般依賴於一個特定類型的實例,但是也可以依賴於類本身。依賴於類型本身的屬性稱爲類型屬性。

可以定義屬性觀察者來監督屬性值的改變,從而作出響應。

存儲屬性

常量屬性let的值在初始化後不能在改變,而變量屬性var的值可以隨時更改。

struct FixedLengthRange {    var firstValue: Int
    let length: Int
}var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
rangeOfThreeItems.firstValue = 6

結構體常量的存儲屬性

如果一個結構體實例被賦值給一個常量,則這個實例所擁有的存儲類型的屬性都不能在改變,包括變量屬性在內。

let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
rangeOfFourItems.firstValue = 6//編譯錯誤,firstValue的值不能改變

延時存儲屬性

延時存儲屬性的初始值直到第一次使用的時候才進行計算,在聲明時通過@lazy進行標記。

提示
一定要將延時存儲屬性聲明爲變量(var),因爲它的初始值可能會在實例初始化完成後纔有,而常量屬性一般在實例初始化完成之前就會有值。

當屬性的初始值依賴於外部銀子,並且該因子的值在實例初始化完成之前不確定時,延時屬性非常有用。如果屬性初始化時需要進行大量的計算,也可以考慮使用延時屬性。

下面是延時屬性的示例:

class DataImporter {    /*
    DataImporter是一個從外部文件導入數據的類。假設它需要花費較多的時間進行初始化
    */
    var fileName = "data.txt"
    //DataImporter類的數據導入功能}

class DataManager {
    @lazy var importer = DataImporter()    var data = String[]()    //DataManager類提供數據管理功能}

let manager = DataManager()
manager.data += "Some data"manager.data += "Some more data"//DataImporter實例還沒有創建

只有訪問DataManagerimporter屬性時纔會去創建這個對象。

println(manager.importer.filename)//創建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.center
square.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)“

這個例子定義了三個結構體來表示幾何形狀:

  • Point封裝了(x, y)座標。

  • Size封裝了寬度和高度。

  • Rect用座標原點和大小定義一個矩形。

其中Rect結構體還提供了一個center的計算屬性。這個屬性的值是由矩形的originsize屬性決定的,它本身並不需要存儲信息。但是改變center的值,會間接的修改矩形的其它屬性。
computedProperties_2x

三、簡化setter的聲明

如果沒有爲計算屬性的setter的新值指定名字,則默認使用newValue。下面是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 聲明計算屬性,包括只讀的計算屬性在內,因爲它們的值是可能改變的。而let只能用於常量的聲明,表示它們的值不能發生改變。

還可以省略只讀計算屬性聲明中的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”

屬性觀察者

屬性觀察者用來觀察和響應屬性值的變化。每次設置屬性的值都會調用相應的觀察者,哪怕是設置相同的值。

可以給除延時存儲屬性以外的任何存儲屬性添加觀察者。通過重寫屬性,可以在子類中給父類的屬性(包括存儲屬性和計算屬性)添加觀察者。

提示
不需要給類本身定義的計算屬性添加觀察者,完全可以在計算屬性的setter中完成對值的觀察。

通過下面兩個方法對屬性進行觀察:

  • willSet在屬性的值發生改變之前調用。

  • didSet在設置完屬性的值後調用。

如果沒有給willSet指定參數的話,編譯器默認提供一個newValue做爲參數。同樣,在didSet中如果沒有提供參數的話,默認爲oldValue

提示
willSetdidSet觀察者在屬性進行初始化的時候不會被調用。

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

提示
如果在didSet中給屬性設置新值,最終結果就是最後設置的這個值。

全局變量與局部變量

上面關於計算屬性和屬性觀察對全局變量和局部變量同樣成立。全局變量定義在任意的方法、函數、閉包或者類型定義之外。而局部變量則定義在方法、函數或閉包之內。

之前遇到的全局變量或者局部變量都跟存儲屬性類型,都是用來存儲值的。但實際上它們也能像計算屬性有計算變量。

提示
全局變量和常量與延時屬性類似,總是延時進行計算。但是它們並不需要使用@lazy標記。
局部常量和變量一定不是延時計算的。

類型屬性

實例屬性屬於某個特定類型的實例。每次創建的實例,它都擁有自己的一組獨立的屬性值,不受其它實例對象影響。

你還可以定義屬於類型本身的屬性。這些屬性是與具體的實例無關的,不管創建多少個實例都只有一份。這種屬性稱之爲類型屬性

可以給值類型(結構體和枚舉類型)定義存儲和計算類型的類屬性,但是隻能給類定義計算類型的類屬性。值類型的存儲屬性可以是變量或常量。

提示
一定要給存儲類型的類屬性設置初始值。

類型屬性語法

在C/Objective-C中只能使用全局靜態變量來定義依賴與某個屬性的變量或常量。但是在Swift中可以直接將它們定義爲類型的一部分。其中結構體和枚舉類型中使用static關鍵字,而在類類型中則使用class關鍵字。

struct SomeStructure {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {        //return an Int value here
    }
}

enum SomeEnumeration {
    static var storedTypeProperty = "Some value."
    static var computedTypeProperty: Int {        //return an Int value here
    }
}

class SomeClass {
    class var computedTypeProperty: Int {        //return an Int value here
    }
}

提示
上面的計算屬性都是隻讀的,但實際上可以定義爲可讀可寫

使用類型屬性

類型屬性通過類型名字和點操作符進行訪問和設置,而不是通過實例對象:

println(SomeClass.computedTypeProperty)//print "42"println(SomeStructure.storedTypeProperty)//prints "Some value"SomeStructure.storedTypeProperty = "Another value."println(SomeStructure.storedTypeProperty)//prints "Another value."

下面演示瞭如何使用一個結構體來對聲道音量進行建模,其中每個聲道音量範圍爲0-10。
staticPropertiesVUMeter_2x

struct AudioChannel {
    static let thresholdLevel = 10
    static var maxInputLevelForAllChannel = 0
    var currentLevel: Int = 0 {
        didSet {            if currentLevel > AudioChannel.thresholdLevel {
                currentLevel = AudioChannel.thresholdLevel
            }            if currentLevel > AudioChannel.maxInputLevelForAllChannels{
                AudioChannel.maxInputLevelForAllChannels = currentLevel
        }
    }
}var leftChannel = AudioChannel()var rightChannel = AudioChannel()

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

當修改其中一個聲道的值時,整個聲道的音量最大值就可能發生改變。而每個聲道都有自己的當前音量水平。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章