swift的class 和struce

翻譯自:Classes and Structures
類和結(jié)構(gòu)體是組成程序代碼的通用的、靈活的結(jié)構(gòu)。通過(guò)使用與常亮、變量和函數(shù)完全相同的語(yǔ)法定義屬性和方法來(lái)為類和結(jié)構(gòu)添加功能。

與其他編程語(yǔ)言不同,Swift不要求您為自定義的類和結(jié)構(gòu)體創(chuàng)建單獨(dú)的接口和實(shí)現(xiàn)文件。在Swift中,您可以在單個(gè)文件中定義一個(gè)類或結(jié)構(gòu),并且該類或結(jié)構(gòu)的外部接口會(huì)自動(dòng)提供給其他代碼使用。

注意
傳統(tǒng)意義上將類的實(shí)例成為對(duì)象。然而,swift類和結(jié)構(gòu)體在功能上比其他語(yǔ)言更接近,而本章介紹了很多可以應(yīng)用到的類和結(jié)構(gòu)體的實(shí)例的功能。

比較類和結(jié)構(gòu)體

Swift中的類和結(jié)構(gòu)體有許多共同之處。兩者都可以:

  • 定義屬性以存儲(chǔ)值
  • 定義提供功能的方法
  • 使用下標(biāo)語(yǔ)法定義下標(biāo)以提供對(duì)其值的訪問(wèn)
  • 定義初始化程序以設(shè)置其初始狀態(tài)
  • 擴(kuò)展到超出默認(rèn)實(shí)現(xiàn)范圍的功能
  • 符合協(xié)議以提供某種標(biāo)準(zhǔn)功能

有關(guān)更多信息, 參閱 Properties, Methods, Subscripts, Initialization, Extensions, and Protocols.

類具有結(jié)構(gòu)體不具有的其它功能:

  • 繼承使一個(gè)類能夠繼承另一個(gè)類的特性。
  • 類型轉(zhuǎn)換使您能夠在運(yùn)行時(shí)檢查和解釋實(shí)例的類型。
  • Deinitializers使類的一個(gè)實(shí)例釋放它分配的任何資源。
  • 引用計(jì)數(shù)允許對(duì)一個(gè)類實(shí)例的多個(gè)引用。

有關(guān)更多信息,請(qǐng)參閱繼承類型轉(zhuǎn)換,取消初始化自動(dòng)引用計(jì)數(shù)。

結(jié)構(gòu)體在代碼中傳遞時(shí)總是被復(fù)制,并且不使用引用計(jì)數(shù)。

定義語(yǔ)法

類和結(jié)構(gòu)具有類似的定義語(yǔ)法。您可以使用class關(guān)鍵字定義類,struct關(guān)鍵字定義結(jié)構(gòu)體。兩者都將其整個(gè)定義放在一對(duì)大括號(hào)中:

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

注意

每當(dāng)你定義一個(gè)新的類或結(jié)構(gòu)時(shí),你都可以有效地定義一個(gè)全新的Swift類型。給出類型UpperCamelCase名稱(如SomeClass和SomeStructure這里)來(lái)匹配標(biāo)準(zhǔn)Swift類型的資本(如String,Int和Bool)。相反,總是給屬性和方法lowerCamelCase名稱(如frameRate和incrementCount)來(lái)區(qū)分它們與類型名稱。

下面是一個(gè)結(jié)構(gòu)定義和類定義的例子:

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

上面的例子定義了一種新的結(jié)構(gòu)體,稱為Resolution,用來(lái)描述基于像素的顯示分辨率。這個(gè)結(jié)構(gòu)體有兩個(gè)存儲(chǔ)屬性,稱為widthheight。存儲(chǔ)的屬性是作為類或結(jié)構(gòu)的一部分捆綁并存儲(chǔ)的常量或變量。這兩個(gè)屬性被推斷為int類型,將它們?cè)O(shè)置為初始整數(shù)值為0。

上面的例子還定義了一個(gè)新類VideoMode,用于描述視頻顯示的特定視頻模式。這個(gè)類有四個(gè)變量存儲(chǔ)的屬性。第一個(gè),resolution是用一個(gè)新的Resolution結(jié)構(gòu)實(shí)例初始化的,它推斷出一個(gè)屬性類型Resolution。對(duì)于其他三個(gè)屬性,VideoMode將使用(意思是“非隔行視頻”)interlaced設(shè)置來(lái)初始化新實(shí)例false,播放幀速率為0.0,以及可選String值為name。該name屬性會(huì)自動(dòng)給出默認(rèn)值nil或“無(wú)name值”,因?yàn)樗强蛇x類型。

類和結(jié)構(gòu)實(shí)例

該Resolution結(jié)構(gòu)定義和VideoMode類定義只說(shuō)明什么Resolution或VideoMode看起來(lái)像。他們自己沒(méi)有描述特定的分辨率或視頻模式。要做到這一點(diǎn),你需要?jiǎng)?chuàng)建一個(gè)結(jié)構(gòu)或類的實(shí)例。
創(chuàng)建實(shí)例的語(yǔ)法對(duì)于結(jié)構(gòu)和類都非常相似:

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

結(jié)構(gòu)和類都為新實(shí)例使用初始化語(yǔ)法。最簡(jiǎn)單的初始化語(yǔ)法形式使用類或結(jié)構(gòu)體的名稱,后跟空括號(hào),如Resolution()VideoMode()。這將創(chuàng)建類或結(jié)構(gòu)的新實(shí)例,并將任何屬性初始化為默認(rèn)值。類和結(jié)構(gòu)初始化在初始化中有更詳細(xì)的描述。

訪問(wèn)屬性

您可以使用點(diǎn)語(yǔ)法訪問(wèn)實(shí)例的屬性。在點(diǎn)語(yǔ)法中,可以在實(shí)例名稱后面立即寫(xiě)入屬性名稱,并用句點(diǎn)(.)分隔,但不帶任何空格:

print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0"

在這個(gè)例子中,someResolution.width指的是someResolution的width屬性,并返回它的默認(rèn)初始值0。

您可以深入查看子屬性,例如 VideoMode的resolution屬性中的 width屬性:

print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0"

您也可以使用點(diǎn)語(yǔ)法為變量屬性指定一個(gè)新值:

someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280"

與Objective-C不同的是,Swift使您能夠直接設(shè)置結(jié)構(gòu)屬性的子屬性。在上面的最后一個(gè)例子中,someVideoMode 的resolutionproperty 的width屬性是直接設(shè)置的,不需要將整個(gè)resolution屬性設(shè)置為新值。

結(jié)構(gòu)體類型的成員初始化程序#

所有結(jié)構(gòu)都有一個(gè)自動(dòng)生成的成員初始化程序,您可以使用它初始化新結(jié)構(gòu)實(shí)例的成員屬性。新實(shí)例屬性的初始值可以按名稱傳遞給成員初始值設(shè)定項(xiàng):

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

與結(jié)構(gòu)不同,類實(shí)例不會(huì)接收默認(rèn)的成員初始值設(shè)定項(xiàng)。初始化中更詳細(xì)地描述在初始化。

結(jié)構(gòu)和枚舉是值類型

值類型是當(dāng)其被傳遞給函數(shù)時(shí)或者分配給變量或常量時(shí),它的值被拷貝。

事實(shí)上,Swift整數(shù),浮點(diǎn)數(shù),布爾值,字符串,數(shù)組和字典中的所有基本類型都是值類型,并在后臺(tái)實(shí)現(xiàn)為結(jié)構(gòu)結(jié)構(gòu)體。

所有結(jié)構(gòu)和枚舉都是Swift中的值類型。這意味著您創(chuàng)建的任何結(jié)構(gòu)和枚舉實(shí)例以及它們具有的任何值類型都會(huì)在您的代碼中傳遞時(shí)始終進(jìn)行復(fù)制。

考慮這個(gè)例子,它使用Resolution了前面例子中的結(jié)構(gòu):

let hd = Resolution(width: 1920, height: 1080)
var cinema = hd

此示例聲明了一個(gè)常量hd,并將其設(shè)置為使用Resolution全高清視頻的寬度和高度(1920像素寬1080高像素)初始化的實(shí)例。

然后它聲明一個(gè)變量cinema,并將其設(shè)置為當(dāng)前值hd。因?yàn)镽esolution是一個(gè)結(jié)構(gòu),現(xiàn)有實(shí)例的一個(gè)副本被創(chuàng)建,并且這個(gè)新副本被分配給cinema。雖然hd和cinema現(xiàn)在有相同的寬度和高度,他們是幕后兩種完全不同的情況。

接下來(lái),將width屬性cinema修改為用于數(shù)字電影投影的寬度稍寬的2K標(biāo)準(zhǔn)(2048像素寬和1080像素高):

cinema.width = 2048

檢查它的width屬性cinema顯示它確實(shí)已更改為2048:

print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide"

但是,width原始hd實(shí)例的屬性仍具有以下舊值1920:

print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide"

當(dāng)cinema給出當(dāng)前值時(shí)hd,存儲(chǔ)在其中的值hd被復(fù)制到新cinema實(shí)例中。最終結(jié)果是兩個(gè)完全分離的實(shí)例,它們恰好包含相同的數(shù)值。由于它們是單獨(dú)的實(shí)例,因此設(shè)置寬度cinema以2048不影響存儲(chǔ)的寬度hd。

相同的行為適用于枚舉:

enum CompassPoint {
    case north, south, east, west
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection = .east
if rememberedDirection == .west {
    print("The remembered direction is still .west")
}
// Prints "The remembered direction is still .west"

當(dāng)rememberedDirection賦值的時(shí)候currentDirection,它實(shí)際上被設(shè)置為該值的一個(gè)副本。currentDirection此后更改此值不會(huì)影響存儲(chǔ)在其中的原始值的副本rememberedDirection。

類是引用類型

與值類型不同,引用類型在分配給變量或常量時(shí),或者傳遞給函數(shù)時(shí)不會(huì)被復(fù)制。而是使用對(duì)相同現(xiàn)有實(shí)例的引用而不是副本。

下面是一個(gè)使用VideoMode上面定義的類的示例:

let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0

本示例聲明了一個(gè)新的常量tenEighty,并將其設(shè)置為引用VideoMode該類的新實(shí)例。視頻模式被分配的HD分辨率的副本,1920通過(guò)1080從之前。它被設(shè)置為隔行掃描,并被命名為"1080i"。最后,它被設(shè)置為25.0每秒幀數(shù)的幀速率。

接下來(lái),tenEighty將其分配給一個(gè)新的常量,并調(diào)用alsoTenEighty幀速率alsoTenEighty:

let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0

因?yàn)轭愂且妙愋?,tenEighty并且alsoTenEighty實(shí)際上都引用同一個(gè) VideoMode實(shí)例。實(shí)際上,它們只是同一個(gè)實(shí)例的兩個(gè)不同名稱。

檢查frameRate屬性tenEighty顯示它正確報(bào)告30.0來(lái)自底層VideoMode實(shí)例的新幀速率:

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

請(qǐng)注意,tenEighty和alsoTenEighty聲明為常量,而不是變量。但是,你仍然可以改變tenEighty.frameRate,alsoTenEighty.frameRate。因?yàn)槌A縯enEighty和alsoTenEighty常量的值本身并沒(méi)有改變。tenEighty并且alsoTenEighty它們自己不“存儲(chǔ)” VideoMode實(shí)例 - 相反,它們都指向VideoMode幕后的實(shí)例。它是VideoMode底層的屬性frameRate被更改,而不是常量VideoMode引用的值。

Identity 操作

由于類是引用類型,因此多個(gè)常量和變量可能會(huì)在幕后引用同一個(gè)類的單個(gè)實(shí)例。(結(jié)構(gòu)和枚舉也是如此,因?yàn)樗鼈冊(cè)诜峙浣o常量或變量或傳遞給函數(shù)時(shí)總是被復(fù)制。)

找出兩個(gè)常量或變量是否指向一個(gè)類的完全相同的實(shí)例有時(shí)會(huì)很有用。為了實(shí)現(xiàn)這一點(diǎn),Swift提供了兩個(gè)身份運(yùn)算符:

  • 與(===) 相同

  • 與(!==) 不相同

使用這些運(yùn)算符來(lái)檢查兩個(gè)常量或變量是否引用同一個(gè)單一實(shí)例:

if tenEighty === alsoTenEighty {
    print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."

請(qǐng)注意,“與......相同”(用三個(gè)等號(hào)表示,或者===)并不等同于“等于”(用兩個(gè)等號(hào)表示==):

  • 與......相同”表示類型的兩個(gè)常量或變量指向完全相同的類實(shí)例。

  • “等于”意味著兩個(gè)實(shí)例在值中被認(rèn)為是“相等的”或“等同的”,對(duì)于類型的設(shè)計(jì)者所定義的“相等”的某些適當(dāng)?shù)暮x。
    當(dāng)您定義自己的自定義類和結(jié)構(gòu)時(shí),您有責(zé)任決定兩個(gè)“平等”實(shí)例的合格性。在等價(jià)運(yùn)算符中描述定義您自己的“等于”和“不等于”運(yùn)算符的實(shí)現(xiàn)過(guò)程。

指針

如果您有使用C,C ++或Objective-C的經(jīng)驗(yàn),您可能會(huì)知道這些語(yǔ)言使用指針來(lái)引用內(nèi)存中的地址。引用某個(gè)引用類型的實(shí)例的Swift常量或變量類似于C中的指針,但不是指向內(nèi)存中某個(gè)地址的直接指針,也不需要寫(xiě)一個(gè)asterisk(*)來(lái)指示您是創(chuàng)造一個(gè)參考。相反,這些引用是像Swift中的其他常量或變量一樣定義的。

選擇類和結(jié)構(gòu)

您可以使用類和結(jié)構(gòu)來(lái)定義自定義數(shù)據(jù)類型,以用作程序代碼的構(gòu)建塊。

但是,結(jié)構(gòu)實(shí)例總是按傳遞,而類實(shí)例總是按引用傳遞。這意味著它們適合于不同類型的任務(wù)。在考慮項(xiàng)目所需的數(shù)據(jù)結(jié)構(gòu)和功能時(shí),請(qǐng)確定每個(gè)數(shù)據(jù)結(jié)構(gòu)是應(yīng)該定義為類還是結(jié)構(gòu)。

作為一般指導(dǎo)原則,考慮在適用以下一個(gè)或多個(gè)條件時(shí)創(chuàng)建結(jié)構(gòu)題:

  • 結(jié)構(gòu)的主要目的是封裝一些相對(duì)簡(jiǎn)單的數(shù)據(jù)值。

  • 當(dāng)您指定或傳遞該結(jié)構(gòu)的實(shí)例時(shí),期望封裝值將被復(fù)制而不是被引用是合理的。

  • 結(jié)構(gòu)存儲(chǔ)的任何屬性都是它們自己的值類型,也可能被復(fù)制而不是引用。

  • 該結(jié)構(gòu)不需要繼承其他現(xiàn)有類型的屬性或行為。

良好的結(jié)構(gòu)候選人的例子包括:

  • 幾何形狀的大小,也許封裝一個(gè)width屬性和一個(gè)height屬性,都是類型Double。

  • 一種引用一系列范圍內(nèi)的范圍的方法,也許封裝一個(gè)start屬性和一個(gè)length屬性,兩者都是類型Int。

  • 3D坐標(biāo)系中的一個(gè)點(diǎn),可能是封裝xy以及z每個(gè)類型的屬性Double。

在所有其他情況下,定義一個(gè)類,并創(chuàng)建該類的實(shí)例,以便通過(guò)引用進(jìn)行管理和傳遞。實(shí)際上,這意味著大多數(shù)自定義數(shù)據(jù)結(jié)構(gòu)應(yīng)該是類而不是結(jié)構(gòu)。

字符串,數(shù)組和字典的賦值和復(fù)制行為

在swift中,許多基本的數(shù)據(jù)類型,如String,Array以及Dictionary被實(shí)現(xiàn)為結(jié)構(gòu)體。這意味著如果將字符串,數(shù)組和字典等數(shù)據(jù)分配給新的常量或變量,或者將它們傳遞給函數(shù)或方法時(shí),它們將被復(fù)制。

在OC的Foundation框架中:NSString,NSArray,和NSDictionary為類,而不是結(jié)構(gòu)來(lái)實(shí)現(xiàn)。Foundation中的字符串,數(shù)組和字典始終作為對(duì)現(xiàn)有實(shí)例的引用進(jìn)行分配和傳遞,而不是作為副本。

注意
上面的描述涉及字符串,數(shù)組和字典的“復(fù)制”。您在代碼中看到的行為將始終像發(fā)生副本一樣。但是,Swift僅在絕對(duì)必要時(shí),幕后執(zhí)行實(shí)際的副本。Swift管理所有值復(fù)制以確保最佳性能,并且您不應(yīng)該避免分配嘗試搶占此優(yōu)化。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容