程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Swift——(六)Swift中的值類型

Swift——(六)Swift中的值類型

編輯:C++入門知識

在Swift中,結構體和枚舉類型為值類型(Structures and Enumerations),在Stack Overflow上有這樣一個問題:結構體中的可變屬性只有在使用mutating關鍵字之後才能被內置的方法修改(如果是不可變屬性肯定不可以修改了,這就不用糾結了,不過在Swift中,還有一個例外,那就是構造器Initialization對於結構體和類的常量實例屬性可以在構造器中進行設置,這感覺有點違背常量屬性的含義,仔細想想也可以理解,因為實例化的時候,最後調用的是構造器,所以在構造器之後,實例屬性的值才確定)。

@Author: twlkyao 轉載或者引用請保留此行。

這裡梳理下緣由,有什麼不對的地方還望指出,在解決這個問題之前,有兩個概念需要解釋。

首先,一個值是不是可以修改並不在於它是什麼類型(類還是結構體),而在於它的存儲類型(常量還是變量)。只有變量才可以進行修改。

然後,值類型可以理解為每個屬性都在內存中有自己的一份空間,和其它的實例之間的屬性是沒有關系的,值類型可以理解為一個連續的代碼塊,每創建一個值類型的實例,就相當於將這樣的代碼塊復制了一份,每一份都有自己的相應的屬性的值,如果實例類型是可變的,那麼這個代碼塊中的每個屬性都是可以變的,如果一個實例類型是不可變的,那麼這個代碼塊中的每個屬性也都是不可變的,每個實例類型都必須能夠支持屬性可變和不可變,為了滿足這個條件,Swift將結構體的方法分為兩類,可以修改結構體結構的和不可以修改結構體結構的,修改結構體結構的方法,必須被可變的結構體實例調用;不修改結構體結構的方法,可以被可變和不可變的結構體實例調用,而大多數情況下,使用的是後者,所以有可能蘋果直接將後者作為默認情況,結構體的方法不能夠修改結構體實例的屬性引用類型,可以理解為指針(雖然Swift中沒有指針,就像Java一樣,但是面向對象的語言實際上是把類實例都當指針處理的),指向內存空間中的同一個位置,每創建一個引用類型的實例,就會多一個指針,指向這個內存地址。

好了,下邊來解決這個問題,由於值類型實例可以賦值給變量或者常量,而被賦值的常量或者變量又決定了值類型實例是否可變,進而決定了值類型的實例屬性是否可變,可以理解為值類型的實例屬性有兩種模式,可變和不可變(由屬性的類型和最後被實例賦值的常量或變量決定),以下是規則:

實例屬性可變性規則實例類型(賦值號左邊的類型)實例屬性類型可變性varletletvarvarvarletletletletvarlet


舉例如下:

struct Point {
    var x = 0
    let y = 0
}

var a = Point()
let b = Point()

a.x = 1 // var, right.
//a.y = 2 // let, compile time error.
//b.x = 3 // let, compile time error.
//b.y = 4 // let, compile time error.

如上所示,只有在實例屬性為變量,並且最終實例賦值給一個變量的時候,才可以修改相應變量的屬性,而引用類型在實例化的時候,並沒有進行相應的屬性的復制,只是相當於添加了一個指向相應屬性的指針,而屬性可能又是指向其它類型的指針,所以var類型的屬性還是let類型的屬性,只是確定這個"指針"也就是對應關系是不是可以變

可以理解為在結構體進行實例化之前,結構體並不知道自己是不是可變的,為了防止被誤修改,默認為自己是不可變的,除非在事先聲明的情況才可變,這就是"mutating"關鍵字的作用。

下面給出代碼說明值類型。

struct Point1 {
    var x = 0, y = 0
    mutating func moveToX(x: Int, andY y:Int) { // need to be a mutating method in order to work
        self.x = x
        self.y = y
    }
}

var p1 = Point1(x: 1, y: 2) // in order to change the properties, you have to use var, since it is a value type.
p1.x = 3 // works from outside the struct.
p1.moveToX(5, andY: 5)

println("p1.x = \(p1.x), p1.y = \(p1.y)")

/***************************/
struct Point2 {
    let x = 0, y = 0
}

var p2 = Point2(x: 1, y: 2)
println("p2.x=\(p2.x), p2.y=\(p2.y)")
//p2.x = 3 // can't change p2.x, since p2.x is a constant.

下面給出代碼,說明引用類型:

class Point3 {
    var x = 0
    var y = 0
    let plet: Point4
    var pvar: Point4
    init(x: Int, y: Int) {
        self.x = x
        self.y = y
        self.plet = Point4() // plet.x = 0, plet.y = 0
        self.pvar = Point4() // pvar.x = 0, pvar.y = 0
    }

    func moveToX(x: Int, andY y: Int) { // no need to use "mutating" keyword.
        self.x = x;
        self.y = y;
    }
}

class Point4 {
    var x = 0
    var y = 0
}

let p3 = Point3(x:1, y:2) // you can use let, even though you want to change the property, because it is a reference.
p3.x = 2
p3.moveToX(5, andY: 5) // no need to use the "mutating" keyword.
println("p3.x = \(p3.x), p3.y = \(p3.y)") // x = 5, y = 5

var p4 = p3 // p3 and p4 are the same, since they are reference type.
p4.x = 3
println("p4.x = \(p4.x), p4.y = \(p4.y)") // p4.x = 3, p4.y = 5
println("p3.x = \(p3.x), p3.y = \(p3.y)") // p3.x = 3, p3.y = 5

/**********************/
p3.plet.x = 4
println("p4.p.x = \(p3.plet.x), p4.p.y = \(p3.plet.y)") // p3.plet.x = 4, p3.plet.y = 0

let p5  = Point4()
//p3.plet = p5 // can't assign new value to p3.plet since the realtion can't change since the p property of p3 is a constant.
p3.pvar = p5 // even p3 is a constant, its propery can change.

在上面的代碼中,最重要的就是"*"號以下的代碼,可以說明引用類型中的let和var的作用。

更詳細討論,可以查看:http://stackoverflow.com/questions/24035648/swift-and-mutating-struct

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved