學習Swift筆記 (十)Swift的類和結構

Swift的類和結構

Swift不需要單獨創建接口或者實現文件來使用類或者結構。Swift中的類或者結構可以在單文件中直接定義,一旦定義完成後,就能夠被直接其他代碼使用。

注意:一個類的實例一般被視作一個對象,但是在Swift中,類與結構更像一個函數方法。


1. 類和結構的異同

相似:

· 定義一些可以賦值的屬性。

· 定義具有功能性的方法。

· 定義下標,使用下標語法。

· 定義初始化方法來設置初始狀態

· 在原實現方法的可擴展性。

· 根據協議提供某一特定類別的基本功能


結構不具備的類的一些特性:

· 類的繼承性

· 對類實例實時的類型轉換

· 析構一個類的實例使之釋放空間

· 引用計數,一個類實例可以有多個引用


注意:結構每次在代碼中傳遞時都是複製一整個,所以不要使用引用計數。


定義語法

類的結構擁有相似的定義語法,使用class關鍵字定義一個類,struct關鍵字定義結構。每個定義都由一對大括號包含:

class SomeClass{
    //class definition goes here
}
struct SomeStructure{
    //structure definition goes here
}

注意:在定義類和結構時,一般使用UpperCamelCase命名法來定義類和結構的名稱,比如SomeClass和SomeStructure,這樣也符合Swift其他類型的標準,而給屬性和方法命名時,一般時候lowCamelCase命名法,比如frameRate和incrementCount等。


類和結構的實例

struct Resolution{
    var width = 0
    var height = 0
}
class VideoMode{
    var resolution = Resulution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}

實例化的語法相似:

let someResolution = Resolution()
let someVideoMode = VideoMode()


類和結構都使用實例語法來完成實例化。最簡單的實例語法就是用兩個括號()完成。在這種情況下定義的實例中的屬性都會完成默認初始化。


訪問屬性

使用點語法(.)就可以方便的訪問一個實例的屬性。在點語法中,在實例名之後加上(.)在加上屬性名即可。不需要空格:

println("The width of someResolution is \(someResolution.width)")

也可以使用點語法連續的獲取屬性的屬性:

println("The width of someVideoMode is \(someVideoMode.resolution.width)")


使用這種方法不僅可以訪問,也可以賦值:

someVideoMode.resolution.width = 1280

注意:和OC不同,Swift能夠直接設置一個結構屬性的子屬性。


結構類型的成員初始化方法

每個結構都有一個成員初始化方法,可以在初始化的時候通過使用屬性名稱來制定每一個屬性的初始值:

struct Resolution{
    var width = 0
    var height = 0
}

let vga = Resolution(width: 640, height: 480)


但是和結構不同,類實力不能夠使用成員初始化方法。




2.結構和枚舉類型是數值類型

數值類型是說當它被賦值給一個常量或者變量,或者作爲參數傳遞給函數時,是完整的複製了一個新的數值,而不是僅僅改變了引用對象。

在Swift中所有的結構和枚舉類型都是數值類型。這意味你實例化的每個結構和枚舉,其包含的所有屬性,都會再代碼中傳遞的時候被完整複製。

struct Resolution{
    var width = 0
    var height = 0
}

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
<span style="font-size:24px;font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="html">struct Resolution{
    var width = 0
    var height = 0
}

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
cinema.width = 2048
println("cinema is now \(cinema.width) pixels wide")
//cinema is now 2048 pixels wide
println("cinema is now \(hd.width) pixels wide")
//cinema is now 1920 pixels wide


 當hd被賦值給cinema時,是完整的複製了一個全新的Resolution結構給cinema,所以當cinema的屬性被修改時,hd的屬性不會變化。

enum CompassPoint{
    case North, South, East, West
}
var currentDirection = CompassPoint.West
let rememberedDirection = currentDirection
currentDirection = .East
if rememberedDirection == .West{
    println("The remembered direction is still .West")
}
//The remembered direction is still .West

儘管經過幾次賦值,rememberedDirection依然沒有變化,這是因爲在每一次賦值過程中,都是將數值類型完整的複製過來。


3.  類是引用類型

和數值類型不同引用類型不會複製整個實例,當它被複制給另外一個常量或者變量的時候,而是會建立一個和已有的實例相關的引用來表示它。

struct Resolution{
    var width = 0
    var height = 0
}
class VideoMode{
    var resolution = Resolution()
    var interlaced = false
    var frameRate = 0.0
    var name: String?
}
let hd = Resolution(width: 1920, height: 1080)
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

println("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
//The frameRate property of tenEighty is now 30.0



特徵操作

因爲類是引用類型,那麼就可能存在多個常量或者變量指向同一個類的實例。(這對於數值類型的結構和枚舉是不成立的)。

可以通過如下兩個操作來判斷兩個常量或者變量是否引用的是同一個類的實例:

相同的實例(===)

不同的實例(!==)

if tenEighty === alsoTenEighty {
    println("tenEighty and alsoTenEighty refer to the same Resolution instance.")
}
//tenEighty and alsoTenEighty refer to the same Resolution instance.

(===)實例相同表示的是兩個變量或者常量所引用的是同一個類的實例。

(==)相等是指兩個實例在數值上的相等,或者相同。

當你定義一個類的時候,就需要說明什麼樣的時候是兩個類相同,什麼時候是兩個類不相等。


指針

在C/C++/OC中,使用指針來引用一個內存地址。Swift中引用一個實例的常量或變量和C中的指針類似,但是不是一個直接指向內存地址的指針,也不需要使用*記號表示你正在定義一個引用。Swift中引用和其他變量,常量的定義方法相同。


4.  如果選擇使用類還是結構

在代碼中可以選擇類或者結構來實現你所需要的代碼塊,完成相應的功能。但是結構實例傳遞的是值,而類實例傳遞的是引用。對於不同的任務,應該考慮到數據結構和功能的需求不同,從而選擇不同的實例。


一般來說,下面的一個或多個條件滿足時,應當選擇創建一個結構:

結構主要是用來封裝一些簡單的數據值。

當賦值或者傳遞的時候更希望這些封裝的數據被賦值,而不是被引用過去。

所有被結構存儲的屬性本身也是數值類型。

結構不需要被另外一個類型繼承或者完成其他行爲。


一些比較好的使用結構的例子:

一個幾何形狀的尺寸,可能包括寬度,高度,或者其他屬性。

一個序列的對應關係,可能包括開始start和長度length屬性。

3D座標系中的一個點,包括x,y,z座標。


在其他情況下,類會是更好的選擇。也就是說一般情況下,自定義的一些數據結構一般都會被定義爲類。


5. 集合類型的賦值和複製操作

Swift中,數組Array和字典Dictionary是用結構來實現的,但是數組與字典和其他結構在進行賦值或者作爲參數傳遞給函數的時候有一些不同。

並且數組和字典的這些操作,又與Foundation中的NSArray和NSDictionary不同,它們是用類來實現的。


注意:下面介紹數組,字典,字符串等複製操作。這些複製操作看起來都已經發生,但是Swift只會在確實需要複製的時候纔會完整複製,從而達到最優的性能。


字典的賦值和複製操作

每次將一個字典Dictionary類型賦值給一個常量或者變量,或者作爲參數傳遞給函數時,字典會再賦值或者函數調用時,纔會被複制。

如果字典中的鍵值是數值類型(結構或者枚舉),它們在賦值的時候會同時被複制。相反,如果是引用類型(類或者函數),引用本身將會被複制,而不是類實力或者函數本身。字典的這種複製方式和結構相同。


var ages = ["Peter":23,"Wei":35,"Anish":65,"Katya":19]
var copiedAges = ages

copiedAges["Peter"] = 24
println(ages["Peter"])
//23


數組的賦值和複製操作

和字典Dictionary類型比起來,數組Array的賦值和複製操作就更加複雜。Array類型和C中的類似,僅僅只會在需要的時候纔會完整的複製數組的值。

如果將一個數組賦值給一個常量或者變量,或者作爲一個參數傳遞給函數,複製在賦值和函數調用的時候並不會發生。這兩個數組將會共享一個元素序列,如果你修改了其中一個,另外一個也將會發生改變。

對於數組來說,複製只會在你進行了一個可能會修改數組長度操作時纔會發生。包括拼接,添加或者移除元素等等。當複製實際發生的時候,纔會像字典的賦值和複製操作一樣。

var a = [1,2,3]
var b = a
var c = a
//如果改變a中的某個值,會發現b和c中的數值也會跟着改變,因爲賦值操作沒有改變數組的長度:
a[0] = 42
println(a[0])//42
println(b[0])//42
println(c[0])//42
//但是,如果在a中添加一個新的元素,那麼就改變了數組的長度,這個時候就會發生實際的複製操作。如果在改變a中元素的值,b和c的元素將不會發生變化
a.append(4)
a[0] = 77
println(a[0])//77
println(b[0])//42
println(c[0])//42

設置數組是唯一的

如果可以在對數組進行修改前,將它設置爲唯一的就最好了。我們可以通過  使用unshare方法來講數組自行拷貝出來,成爲一個唯一的實體。

如果多個變量引用了同一個數組,可以使用unshare方法來完成一次“獨立”。

b.unshare()

這時候如果在修改b的值,c的值也不會在受到影響。

var a = [1,2,3]
var b = a
var c = a
//如果改變a中的某個值,會發現b和c中的數值也會跟着改變,因爲賦值操作沒有改變數組的長度:
a[0] = 42
println(a[0])//42
println(b[0])//42
println(c[0])//42
//但是,如果在a中添加一個新的元素,那麼就改變了數組的長度,這個時候就會發生實際的複製操作。如果在改變a中元素的值,b和c的元素將不會發生變化
a.append(4)
a[0] = 77
println(a[0])//77
println(b[0])//42
println(c[0])//42
b.unshare()
b[0] = -105
println(a[0])//77
println(b[0])//-105
println(c[0])//42

檢查兩個數組是否共用了相同的元素

使用實例相等操作符來判斷兩個數組是否共用了元素(===和!==)

var a = [1,2,3]
var b = a
var c = a
//如果改變a中的某個值,會發現b和c中的數值也會跟着改變,因爲賦值操作沒有改變數組的長度:
a[0] = 42
println(a[0])//42
println(b[0])//42
println(c[0])//42
//但是,如果在a中添加一個新的元素,那麼就改變了數組的長度,這個時候就會發生實際的複製操作。如果在改變a中元素的值,b和c的元素將不會發生變化
a.append(4)
a[0] = 77
println(a[0])//77
println(b[0])//42
println(c[0])//42
b.unshare()
b[0] = -105
println(a[0])//77
println(b[0])//-105
println(c[0])//42

if b === c {
    println("b and c still share the same array elements.")
}else{
    println("b and c now refer to two independent sets of array elements.")
}
//b and c now refer to two independent sets of array elements.

也可以使用這個操作來判斷兩個子數組是否有共用的元素:

if b[0...1] === c[0...1]{
    println("These two subarrays share the same element.")
}else{
    println("These two subarrays do not share the same elements.")
}
//These two subarrays do not share the same elements.

強制數組拷貝

通過調用數組的copy方法來完成強制拷貝。這個方法將會完整複製一個數組到新的數組中。

var names = ["Mohsen","Hilary","Justyn","Amy","Rich","Graham","Vic"]
var copiedNames = names.copy()
//通過改變copiedNames的值可以驗證,數組已經被完整拷貝,不會影響到之前的數組:
copiedNames[0] = "Mo"
println(names[0])
println(copiedNames[0])
//Mohsen
//Mo

注意:如果你不確定你需要的數組是否是獨立的,那麼僅僅使用unshare就可以了。而copy方法不管當前是不是獨立的,都會完整拷貝一次,哪怕這個數組已經是unshare的了。







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