繼續學習Swift文檔,從上一章節:枚舉,咱們學習了Swift枚舉相關的內容,如枚舉的定義和使用、CaseIterable關鍵字、關聯值、原始值、indirect關鍵字聲明遞歸枚舉等這些內容。如今,咱們學習Swift的結構體和類相關的內容。因爲篇幅較長,這裏分篇來記錄,接下來,Fighting!html
插一句話,若是你已經熟練掌握告終構體和類的使用,那麼,請移步下一章節:屬性編程
結構體和類是靈活的通用結構,它們是程序代碼的構建塊。 您能夠定義屬性和方法,使用與定義常量,變量和函數相同的語法向結構體和類添加功能。swift
與其餘編程語言不一樣,Swift不須要您爲自定義結構體和類建立單獨的接口和實現文件。 在Swift中,您能夠在一個文件中定義結構體或類,而且該類或結構體的外部接口會自動提供給其餘代碼使用。數組
注意
傳統上將類的實例稱爲對象。 可是,Swift結構體和類在功能上比其餘語言要緊密得多,本章的大部份內容描述了適用於類或結構體類型的實例的功能。 所以,使用了更通用的術語實例。bash
Swift中的結構體和類有不少共同點。 二者均可以:app
更多信息,請參閱Properties, Methods, Subscripts, Initialization, Extensions, 和 Protocols.編程語言
類具備結構體所沒有的其餘功能:ide
更多信息,請參見 Inheritance, Type Casting, Deinitialization, 和 Automatic Reference Counting.函數
類支持的附加功能是以增長複雜性爲代價的。 做爲通常準則,應首選結構體,由於它們更易於推理,並在適當或必要時使用類。 實際上,這意味着您定義的大多數自定義數據類型將是結構體和枚舉。 有關更詳細的比較,請參見Choosing Between Structures and Classes。post
結構體和類具備類似的定義語法。 您可使用struct關鍵字定義結構,使用class關鍵字定義類。 二者都將它們的整個定義放在一對大括號中:
struct SomeStructure {
// structure definition goes here
}
class SomeClass {
// class definition goes here
}
複製代碼
注意
每當定義新的結構或類時,就定義新的Swift類型。 提供類型UpperCamelCase的名稱(例如此處的SomeStructure和SomeClass)以匹配標準Swift類型(例如String,Int和Bool)的大小寫。 給屬性和方法lowerCamelCase名稱(例如frameRate和incrementCount)以將它們與類型名稱區分開。
下面是結構體和類的定義:
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
複製代碼
上面的示例定義了一個稱爲「Resolution」的結構體,用於描述基於像素的顯示分辨率。該結構具備兩個存儲的屬性,稱爲寬度和高度。存儲的屬性是常量或變量,它們捆綁在一塊兒並存儲爲結構體或類的一部分。經過將它們設置爲初始整數值0,能夠推斷這兩個屬性爲Int類型。
上面的示例還定義了一個稱爲VideoMode的類,以描述用於視頻顯示的特定視頻模式。此類具備四個變量存儲的屬性。第一個分辨率是使用Resolution結構體實例初始化的,該實例能夠推斷Resolution的屬性類型。對於其餘三個屬性,VideoMode實例將使用interlaced設置false(意爲「非隔行視頻」),frameRate爲0.0以及可選的名爲name的String值進行初始化。名稱屬性會自動提供默認值nil或「無名稱值」,由於它是可選類型。
Resolution結構體定義和VideoMode類定義僅描述Resolution或VideoMode的外觀。 它們自己並無描述特定的分辨率或視頻模式。 爲此,您須要建立結構體或類的實例。
對於結構體和類,建立實例的語法很是類似:
let someResolution = Resolution()
let someVideoMode = VideoMode()
複製代碼
結構體和類都對新的實例使用初始化程序語法。 初始化程序語法的最簡單形式是使用類或結構體的類型名稱,後跟空括號,例如Resolution()或VideoMode()。 這將建立類或結構體的實例,並將全部屬性初始化爲其默認值。 類和結構體的初始化在Initialization中有更詳細的描述。
您可使用點語法訪問實例的屬性。 在點語法中,您應在實例名稱以後當即寫上屬性名稱,並用句點(.)分隔,不能有任何空格:
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"
複製代碼
在此示例中,someResolution.width引用someResolution的width屬性,並返回其默認初始值0。
您能夠深刻了解子屬性,例如VideoMode的resolution屬性中的width屬性:
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"
複製代碼
您還可使用點語法將新值分配給變量屬性:
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"
複製代碼
全部結構體都有一個自動生成的成員初始化程序,您可使用該初始化程序初始化結構體實例的成員屬性。 能夠經過名稱將新實例的屬性的初始值傳遞給成員初始化器:
let vga = Resolution(width: 640, height: 480)
複製代碼
與結構不一樣,類實例不會接收默認的成員初始化器。 初始化程序在Initialization中有更詳細的描述。
值類型是一種在將值分配給變量或常量或將其傳遞給函數時會複製其值的類型。
實際上,在前幾章中,您一直在普遍使用值類型。 實際上,Swift中的全部基本類型(整數,浮點數,布爾值,字符串,數組和字典)都是值類型,並在幕後實現爲結構體。
全部結構體和枚舉都是Swift中的值類型。 這意味着您建立的任何結構體和枚舉實例以及它們具備的任何值類型做爲屬性,都將在它們在代碼中傳遞時始終會被複制。
注意
由標準庫定義的集合(例如數組,字典和字符串)使用了優化來下降複製的性能成本。 這些集合不是當即進行復制,而是共享內存,這些存儲在原始實例和任何副本之間存儲元素。 若是修改了集合的副本之一,則在修改以前就將元素複製。 您在代碼中看到的行爲始終就像是當即進行了複製同樣。
思考如下示例,該示例使用上一個示例中的Resolution結構體:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
複製代碼
本示例聲明一個名爲hd的常量,並將其設置爲一個Resolution實例,該實例使用全高清視頻的寬度和高度(1920像素寬x 1080像素高)初始化。
而後,它聲明一個名爲cinema的變量,並將其設置爲hd的當前值。 由於Resolution是一種結構體,因此將複製現有實例,並將該新副本分配給cinema。 儘管hd和cinema如今具備相同的寬度和高度,但它們是幕後的兩個徹底不一樣的實例。
接下來,將電影院的width屬性修改成用於數字電影院投影的稍寬的2K標準的寬度(寬2048像素,高1080像素):
cinema.width = 2048
複製代碼
檢查cinema的width屬性代表它的確已更改成2048:
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"
複製代碼
可是,原始hd實例的width屬性仍然是1920的舊值:
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"
複製代碼
當給cinema提供hd的當前值時,存儲在hd中的值將被複制到新的cinema實例中。 最終結果是兩個徹底獨立的實例,其中包含相同的數值。 可是,因爲它們是單獨的實例,所以將cinema的寬度設置爲2048不會影響以hd存儲的寬度,以下圖所示:
枚舉有相同的行爲:
enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// Prints "The current direction is north"
// Prints "The remembered direction is west"
複製代碼
爲RememberedDirection分配currentDirection的值時,它其實是設置爲該值的副本。 此後更改currentDirection的值不會影響存儲在RememberedDirection中的原始值的副本。
與值類型不一樣,將引用類型分配給變量或常量或將其傳遞給函數時,不會複製引用類型。 不是副本,而是使用對相同現有實例的引用。
這是一個使用上面定義的VideoMode類的示例:
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
複製代碼
本示例聲明一個名爲tenEighty的新常量,並將其設置爲引用VideoMode類的新實例。 在視頻模式以前,已爲其分配了1920 x 1080高清分辨率的副本。 它設置爲隔行掃描,其名稱設置爲「 1080i」,其幀速率設置爲每秒25.0幀。
接下來,將tenEighty分配給一個新的常量,也稱爲alsoTenEighty,並修改alsoTenEighty的幀速率:
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
複製代碼
因爲類是引用類型,所以tenEighty和alsoTenEighty實際上都引用相同的VideoMode實例。 實際上,它們只是同一單個實例的兩個不一樣名稱,以下圖所示:
檢查tenEighty的frameRate屬性代表它正確地打印了來自VideoMode實例的新幀速率30.0:
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0"
複製代碼
此示例還顯示了爲什麼難以推理引用類型。若是tenEighty和alsoTenEighty在程序代碼中相距甚遠,可能很難找到改變視頻模式的全部方式。不管在何處使用tenEighty,都還必須考慮使用alsoTenEighty的代碼,反之亦然。相反,值類型更容易推論,由於與同一值交互的全部代碼在源文件中都在一塊兒。
請注意,tenEighty和alsoTenEighty被聲明爲常量,而不是變量。可是,您仍然能夠更改tenEighty.frameRate和alsoTenEighty.frameRate,由於tenEighty和alsoTenEighty常量自己的值實際上並無改變。 tenEighty和alsoTenEighty自己並不「存儲」 VideoMode實例,相反,它們都引用了幕後的VideoMode實例。更改的是基礎VideoMode的frameRate屬性,而不是對該VideoMode的常量引用的值的更改。
因爲類是引用類型,所以多個常量和變量有可能在後臺引用同一類的單個實例。 (對於結構體和枚舉,狀況並不是如此,由於在將它們分配給常量或變量或傳遞給函數時,它們老是被複制。)
有時找出兩個常量或變量是否引用了類的徹底相同實例有時會頗有用。 爲此,Swift提供了兩個身份運算符:
使用這些運算符檢查兩個常量或變量是否引用相同的單個實例:
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
複製代碼
請注意,與(用三個等號或===表示)Identical to並不意味着和equal to (用兩個等號或==表示)相同。 Identical to表示類類型的兩個常量或變量引用徹底相同的類實例。 equal to 表示兩個實例的值相等,對於某些適當的含義,如類型設計者所定義的那樣。
自定義結構體和類時,您有責任肯定兩個實例相等時的資格。 等價運算符中介紹了定義==和!=運算符的實現的過程。
若是您有使用C,C ++或Objective-C的經驗,您可能會知道這些語言使用指針來引用內存中的地址。 引用某種引用類型的實例的Swift常量或變量相似於C中的指針,但不是指向內存中地址的直接指針,而且不須要您編寫星號(*)來表示您正在建立一個引用。 相反,這些引用的定義與Swift中的其餘任何常量或變量同樣。 若是須要直接與指針進行交互,則標準庫提供了可使用的指針和緩衝區類型-請參見Manual Memory Management。
這一章節的內容來個小結:
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
複製代碼
hd和cinema是兩個不一樣的實例,修改其中一個實例的屬性值,不會影響另外一個實例。
最後說一句,喜歡的朋友麻煩你手抖一下,點個贊哦,謝謝啦,嘿嘿~