Initialization in Swift

簡介

*自定義構造過程

*默認構造器

*值類型的構造器代理

*類的繼承和構造過程

*可失敗構造器

*必需構造器

*通過閉包和函數來設置屬性的默認值

(2014-8-8更新至beta5語法)

(2014-10-24更新至Swift1.1)

##簡介

構造過程是為了使用某個類、結構體或枚舉類型的實例而進行的準備過程。這個過程包含了為實例中的每個屬性設置初始值和為其執行必要的準備和初始化任務。

Swift中的構造器不像Objective-C那樣有返回值,但是跟C++有點像:所有的構造器都以init命名,用參數列表來區分各個構造器

structFahrenheit{

var temperature =16.0

init() {

temperature =32.0

}

}

var f =Fahrenheit()

Fahrenheit是一個結構體,與類一樣,Swift中的值類型也有構造器和普通方法。上面的代碼先是定義了temperature屬性的默認值,然后又在構造器中將其賦值,最后temperature屬性的值為32。對于temperature這種存儲型屬性,無論定義默認值還是在構造器中賦值,最終它們實現的效果是一樣的。

給存儲型類型屬性賦默認值或在初始構造器中設置初始值時,此屬性的屬性觀察者不會被調用

##自定義構造過程

你可以定義一些帶參數的構造器

structCelsius{

var temperatureInCelsius:Double=0.0

init(fromFahrenheit fahrenheit:Double) {

temperatureInCelsius = (fahrenheit -32.0) /1.8

}

init(fromKelvin kelvin:Double) {

temperatureInCelsius = kelvin -273.15

}

}


構造器的參數也跟Swift中的方法定義(注意不是函數)一樣,也分內部和外部參數名。上面代碼中兩個構造器外部參數名不同,調用構造器的時候也是通過外部參數名來區分的:

let boilingPointOfWater = Celsius(fromFahrenheit:212.0)

// boilingPointOfWater.temperatureInCelsius 是 100.0

let freezingPointOfWater = Celsius(fromKelvin:273.15)

// freezingPointOfWater.temperatureInCelsius 是 0.0”

如果不寫外部參數名那么外部參數名就等于內部參數名:

struct Color {

let red=0.0,green=0.0,blue=0.0

init(red: Double,green: Double,blue: Double) {

self.red=red

self.green=green

self.blue=blue

}

}

let magenta = Color(red:1.0,green:0.0,blue:1.0)

如果你不希望為構造器的某個參數提供外部名字,你還可以使用下劃線_來顯示描述它的外部名:

struct Celsius{

var temperatureInCelsius:Double

init(fromFahrenheit fahrenheit:Double) {

temperatureInCelsius = (fahrenheit -32.0) /1.8

}

init(fromKelvin kelvin:Double) {

temperatureInCelsius = kelvin -273.15

}

init(_celsius:Double) {

temperatureInCelsius = celsius

}

}

let bodyTemperature =Celsius(37.0)

// bodyTemperature.temperatureInCelsius is 37.0

你會發現構造器的第一個內部參數名也會默認作為其外部參數名供調用,這一點與方法不同(方法不會默認將第一個內部參數名作為外部參數名使用),因為方法名可以在結尾加上介詞來烘托出第一個參數的名字,這樣就不需要為第一個參數弄一個外部參數名了,但是構造器只能用init關鍵字來定義。

如果你定制的類型包含一個邏輯上允許取值為空的存儲型屬性–不管是因為它無法在初始化時賦值,還是因為它可以在之后某個時間點可以賦值為空–你都需要將它定義為可選類型optional type。可選類型的屬性將自動初始化為空nil,表示這個屬性是故意在初始化時設置為空的。

class SurveyQuestion {

let text:String

var response:String?

init(text:String) {

self.text=text

}

func ask() {

println(text)

}

}

let cheeseQuestion = SurveyQuestion(text:"Do you like cheese?")

cheeseQuestion.ask()

// 輸出 "Do you like cheese?"

cheeseQuestion.response ="Yes, I do like cheese.

調查問題在問題提出之后,我們才能得到回答。所以我們將屬性回答response聲明為String?類型,或者說是可選字符串類型optional String。當SurveyQuestion實例化時,它將自動賦值為空nil,表明暫時還不存在此字符串。

只要在構造過程結束前常量的值能確定,你可以在構造過程中的任意時間點修改常量屬性的值。盡管text屬性是常量,我們仍然可以在其類的構造器中設置它的值。對某個類實例來說,它的常量屬性只能在定義它的類的構造過程中修改;不能在子類中修改。

##默認構造器

Swift將為所有屬性已提供默認值的且自身沒有定義任何構造器的結構體或基類,提供一個默認的構造器。這個默認構造器將簡單的創建一個所有屬性值都設置為默認值的實例:

class ShoppingListItem{

var name:String?

var quantity =1

var purchased =false

}

var item = ShoppingListItem()

ShoppingListItem類沒有父類(是基類),所有屬性都有默認值(可選屬性默認值為nil),所以可以直接調用默認的無參數構造器來初始化。

除上面提到的默認構造器,如果結構體對所有存儲型屬性提供了默認值且自身沒有提供定制的構造器,它們能自動獲得一個逐一成員構造器(Memberwise Initializers)

struct Size {

varwidth=0.0,height=0.0

}

let twoByTwo = Size(width:2.0,height:2.0)

逐一成員構造器是用來初始化結構體新實例里成員屬性的快捷方法。我們在調用逐一成員構造器時,通過與成員屬性名相同的參數名進行傳值來完成對成員屬性的初始賦值。

##值類型的構造器代理

構造器可以通過調用其它構造器來完成實例的部分構造過程。這一過程稱為構造器代理,它能減少多個構造器間的代碼重復:

struct Size {

varwidth=0.0, height =0.0

}

struct Point {

varx =0.0, y =0.0

}

struct Rect {

varorigin = Point()

varsize = Size()

init() {}

init(origin: Point, size: Size) {

self.origin= origin

self.size= size

}

init(center: Point, size: Size) {

let originX = center.x- (size.width/2)

let originY = center.y- (size.height/2)

self.init(origin: Point(x: originX, y: originY), size: size)

}

}

在值類型中,如果你添加了自定義構造器(如init(center: Point, size: Size)),Swift不會再為結構體生成一個默認構造器和逐一成員構造器,所以我們自己定義了init()和init(origin: Point, size: Size),他們與自動生成的默認構造器和逐一成員構造器是一樣的。這樣子會顯得很麻煩,我們可以將自定義構造器init(center: Point, size: Size)寫在結構體Rect的擴展(extension)里,這樣就不用自己把默認構造器和逐一成員構造器寫一遍了。

構造器init(center:size:)先通過center和size的值計算出origin的坐標。然后再調用(或代理給)init(origin:size:)構造器來將新的origin和size值賦值到對應的屬性中。因為值類型(結構體和枚舉類型)不支持繼承,所以構造器代理的過程相對簡單,因為它們只能代理給本身提供的其它構造器:你只能在構造器內部調用self.init

##類的繼承和構造過程

類里面的所有存儲型屬性–包括所有繼承自父類的屬性–都必須在構造過程中設置初始值。

Swift 提供了兩種類型的類構造器來確保所有類實例中存儲型屬性都能獲得初始值,它們分別是指定構造器(Designated Initializers)和便利構造器(Convenience Initializers)。

###指定構造器和便利構造器

指定構造器是類中最主要的構造器。一個指定構造器將初始化類中提供的所有屬性,并根據父類鏈往上調用父類的構造器來實現父類的初始化。每一個類都必須擁有至少一個指定構造器。

便利構造器是類中比較次要的、輔助型的構造器。你可以定義便利構造器來調用同一個類中的指定構造器,并為其參數提供默認值。你也可以定義便利構造器來創建一個特殊用途或特定輸入的實例。

類的指定構造器的寫法跟值類型簡單構造器一樣,便利構造器也采用相同樣式的寫法,但需要在init關鍵字之前放置convenience關鍵字:

convenience init(parameters) {

statements

}

###構造器鏈

為了簡化指定構造器和便利構造器之間的調用關系,Swift 采用以下三條規則來限制構造器之間的代理調用:

指定構造器必須調用其直接父類的的指定構造器。

便利構造器必須調用同一類中定義的其它構造器。

便利構造器必須最終以調用一個指定構造器結束。

一個更方便記憶的方法是:

指定構造器必須總是向上代理

便利構造器必須總是橫向代理


舉個栗子:

class Food{

var name:String

init(name:String) {

self.name = name

}

convenienceinit() {

self.init(name:"[Unnamed]")

}

}

letnamedMeat =Food(name:"Bacon")

// namedMeat 的名字是 "Bacon”

letmysteryMeat =Food()

// mysteryMeat 的名字是 [Unnamed]


###兩段式構造過程

Swift 中類的構造過程包含兩個階段。第一個階段,每個存儲型屬性通過引入它們的類的構造器來設置初始值。當每一個存儲型屬性值被確定后,第二階段開始,它給每個類一次機會在新實例準備使用之前進一步定制它們的存儲型屬性。

下圖展示了在假定的子類和父類之間構造的階段1:


指定構造器將確保所有子類的屬性都有值,然后它將調用父類的指定構造器,并沿著造器鏈一直往上完成父類的構建過程。一旦父類中所有屬性都有了初始值,實例的內存被認為是完全初始化,而階段1也已完成。

以下展示了相同構造過程的階段2:


父類中的指定構造器現在有機會進一步來定制實例(盡管它沒有這種必要)。

一旦父類中的指定構造器完成調用,子類的構指定構造器可以執行更多的定制操作(同樣,它也沒有這種必要)。

最終,一旦子類的指定構造器完成調用,最開始被調用的便利構造器可以執行更多的定制操作。

兩段式構造過程是基于安全檢查的,可以簡單的理解為:

指定構造器初始化順序:初始化類自己引入的屬性->向上代理調用父類指定構造器->為繼承的屬性設置新值

便利構造器初始化順序:代理調用同一類中的其它構造器->為任意屬性賦新值

構造器在第一階段構造完成之前,不能調用任何實例方法、不能讀取任何實例屬性的值,也不能引用self的值。

###構造器的繼承和重載

跟 Objective-C 中的子類不同,Swift 中的子類不會默認繼承父類的構造器。這是為了防止你想初始化一個很牛逼的類,但是調用的卻是它繼承于父類的菜逼構造器,那將會是個悲劇。

但是如果特定條件可以滿足,父類構造器是可以被自動繼承的:

如果子類沒有定義任何指定構造器,它將自動繼承所有父類的指定構造器。

如果子類提供了所有父類指定構造器的實現–不管是通過規則1繼承過來的,還是通過自定義實現的–它將自動繼承所有父類的便利構造器。

即使你在子類中添加了更多的便利構造器,這兩條規則仍然適用。子類可以通過定義便利構造器來實現父類中的指定構造器,來部分滿足規則2

如果你需要在子類中重寫一個父類的指定構造器(包括自動生成的默認構造器),無論子類中的構造器是指定構造器還是便利構造器,都需要在子類定義重載的構造器前加上override修飾;如果你需要在子類中重寫一個父類的便利構造器,根據構造器鏈,父類的便利構造器不會被子類直接調用,所以不必在子類重寫構造器的定義前用override修飾。這是Xcode6beta5新修訂的規則,在以前的版本中重載構造器不用override修飾。

還記得之前指定的Food類吧,現在它多了一個子類RecipeIngredient:

class RecipeIngredient:Food{

var quantity:Int

init(name:String, quantity:Int) {

self.quantity = quantity

super.init(name: name)

}

override convenience init(name:String) {

self.init(name: name, quantity:1)

}

}

可以看出來RecipeIngredient類的指定構造器和便利構造器都符合兩段式構造安全檢查,并且便利構造器跟Food類中的指定構造器具有相同的參數,盡管RecipeIngredient這個構造器是便利構造器,RecipeIngredient依然提供了對所有父類指定構造器的實現。因此,RecipeIngredient也能自動繼承了所有父類的便利構造器(也就是init()):


上圖中RecipeIngredient類繼承的init()函數版本跟Food提供的版本是一樣的,除了它是將任務代理給RecipeIngredient版本的init(name: String)而不是Food提供的版本。

食材都已經建立好了,下面開始采購吧!我們需要一個購物單,購物單中每一項是這樣子的:

class ShoppingListItem:RecipeIngredient {

var purchased =false

var description: String {

var output ="\(quantity) x \(name.lowercaseString)"

output += purchased ?" ?":" ?"

returnoutput

}

}

由于它為自己引入的所有屬性都提供了默認值,并且自己沒有定義任何構造器,ShoppingListItem將自動繼承所有父類中的指定構造器和便利構造器。


你可以使用全部三個繼承來的構造器來創建ShoppingListItem的新實例:

var breakfastList = [

ShoppingListItem(),

ShoppingListItem(name:"Bacon"),

ShoppingListItem(name:"Eggs", quantity:6),

]

breakfastList[0].name="Orange juice"

breakfastList[0].purchased= true

for item in breakfastList {

println(item.description)

}

// 1 x orange juice ?

// 1 x bacon ?

// 6 x eggs ?

##可失敗構造器(Failable Initializers)

有時候定義一個構造器可以失敗的類,結構體或枚舉是很有用的。這樣的失敗可能被非法的初始化參數,所需外部資源的缺失或一些其他阻止初始化成功的情況觸發。

為了應付初始化可能失敗的情形,需要在定義類,結構體或枚舉時再定義一個或多個可失敗構造器。在init關鍵字后面放一個問號(init?)即可寫出一個可失敗構造器。

你不能定義具有相同參數類型和參數名稱的可失敗構造器和非可失敗構造器

可失敗構造器返回它初始化類型的可選值。你要在可失敗構造器中可能觸發失敗的地方返回nil。

嚴格的來說,構造器不返回值。更確切地說,它們的角色是確保self在構造過程結束時被完整正確的初始化。雖然你寫下return nil來觸發一個構造過程的失敗,但你不要使用return關鍵字來指明構造過程的成功。

下面的例子定義了一個叫做Animal的結構體,它有一個叫做species的String常量屬性。Animal結構體還定義了一個可失敗構造器,它只接收一個species參數。這個構造器檢查被傳遞過來的species參數是否為空字符串:如果是空字符串那就觸發構造過程失敗,否則species屬性會被賦值,構造過程成功:

struct Animal{

let species:String

init?(species:String) {

if species.isEmpty {return nil }

self.species = species

}

}

你可以試著使用可失敗構造器來初始化一個Animal對象來檢驗構造過程是否成功:

let someCreature =Animal(species:"Giraffe")

// someCreature is of type Animal?, not Animal

if let giraffe = someCreature {

println("An animal was initialized with a species of\(giraffe.species)")

}

// prints "An animal was initialized with a species of Giraffe"

如果你給可失敗構造器傳遞一個空字符串作為參數,那么將會觸發構造過程失敗:

let anonymousCreature =Animal(species:"")

// anonymousCreatureisoftypeAnimal?,notAnimal

if anonymousCreature ==nil{

println("The anonymous creature could not be initialized")

}

// prints"The anonymous creature could not be initialized"

這里注意下空字符串與nil的區別。空字符串是合法的,它并不像nil那樣代表了某種類型值得缺失。但在Animal中我們必須設定一個有意義的species屬性,它不能是空字符串。

###枚舉的可失敗構造器

你可以使用可失敗構造器根據一或多個參數來選擇合適的枚舉成員。如果傳入的參數沒能匹配到合適的枚舉成員,構造就會失敗。

下面的例子定義了一個叫做TemperatureUnit的枚舉,有三個可能的情況 (Kelvin,Celsius, 和Fahrenheit)。可失敗構造器用于在表現一個溫度符號字符時找到一個合適的枚舉成員:

enum TemperatureUnit{

case Kelvin, Celsius, Fahrenheit

init?(symbol:Character) {

switch symbol {

case"K":

self= .Kelvin

case"C":

self= .Celsius

case"F":

self= .Fahrenheit

default:

returnnil

}

}

}

你可以使用這個可失敗構造器來為三種可能的狀況選擇合適的枚舉成員,也可在參數不能匹配任何狀況時讓構造過程失敗:

let fahrenheitUnit =TemperatureUnit(symbol:"F")

if fahrenheitUnit !=nil{

println("This is a defined temperature unit, so initialization succeeded.")

}

// prints "This is a defined temperature unit, so initialization succeeded."

let unknownUnit =TemperatureUnit(symbol:"X")

if unknownUnit ==nil{

println("This is not a defined temperature unit, so initialization failed.")

}

// prints "This is not a defined temperature unit, so initialization failed."

###帶有原始值枚舉的可失敗構造器

帶有原始值的枚舉會自動得到一個可失敗構造器:init?(rawValue:),它接受rawValue參數來匹配一個枚舉成員,如果無法匹配就出發構造過程失敗。

發揮init?(rawValue:)的優勢將上面的例子TemperatureUnit重寫:

enum TemperatureUnit:Character{

case Kelvin="K",Celsius="C",Fahrenheit="F"

}

let fahrenheitUnit =TemperatureUnit(rawValue:"F")

if fahrenheitUnit !=nil{

println("This is a defined temperature unit, so initialization succeeded.")

}

// prints "This is a defined temperature unit, so initialization succeeded."

let unknownUnit =TemperatureUnit(rawValue:"X")

if unknownUnit ==nil{

println("This is not a defined temperature unit, so initialization failed.")

}

// prints "This is not a defined temperature unit, so initialization failed."

###類的可失敗構造器

值類型的可失敗構造器可以在任何地方觸發構造過程失敗。在上面的Animal結構體例子中,構造器在species屬性被賦值前就觸發了構造過程失敗,位置很靠前。

對于類,可失敗構造器只能在所有存儲型屬性被賦初值并且任意構造器代理發生后才觸發構造過程失敗。

下面給出的例子展現了如何使用隱式解析可選屬性來滿足這個可失敗類構造器的需求:

class Product{

let name: String!

init?(name: String) {

if name.isEmpty {return nil }

self.name=name

?}

}

上面定義的Product類非常類似之前見過的Animal結構體。Product類有一個不能為空值的字符串常量屬性name。為了強制滿足這個要求,Product類使用可失敗構造器來確保name屬性值在構造過程成功前不為空。

然而Product是一個類而不是結構體。這意味著它不像Animal,任何Product類的可失敗構造器都必須在觸發構造過程失敗之前為name屬性提供一個初值。

在上面的例子中,Product類的name屬性被定義成隱式解析可選的字符串類型(String!)。因為它是一個可選類型,這意味著構造過程中name屬性在被賦值前有一個默認值nil。這個默認值nil意味著Product類中所有屬性都有一個合法的初值。結果就是Product類的可失敗構造器如果被傳遞的是一個空字符串,它能在構造器開始時就觸發一個構造過程失敗。

因為name屬性是常量,你可以確信在構造過程成功后它將永遠不是nil。盡管它被定義為隱式解析可選類型,你可以永遠自信地獲取它的隱式解析值,而不用檢驗是否為nil:

if let bowTie = Product(name:"bow tie") {

// no need tocheckif bowTie.name == nil

println("The product's name is \(bowTie.name)")

}

// prints"The product's name is bow tie"

###可失敗構造器的傳播

類,結構體或枚舉的可失敗構造器可以橫向代理給同一個的類,結構體或枚舉中的另一個可失敗構造器。類似的,子類的可失敗構造器也能向上代理到超類的可失敗構造器。

不論發生何種情況,如果你代理給的另一個構造器發生了構造過程失敗,整個構造過程程序立刻失敗,后續的構造過程代碼也不會執行。

可失敗構造器也可代理給非可失敗構造器。如果你想在一個已經存在的不會失敗的構造過程中添加一個可能失敗的狀況時可以用到這招

下面的例子定義了一個Product的子類CartItem。CartItem類模擬了在線購物車中的一件物品。CartItem包含一個叫做quantity的常量屬性并確保這個屬性總是不小于1:

class CartItem:Product{

let quantity:Int!

init?(name:String, quantity:Int) {

super.init(name: name)

if quantity <1{returnnil}

self.quantity = quantity

?}

}

quantity屬性類型為隱式解析整型(Int!)。就像Product類的name屬性一樣,這意味著quantity屬性在構造過程中被賦值前有一個默認值nil。

CartItem的可失敗構造器開始于向上代理到超類Product的init(name:)構造器。這滿足了要求–可失敗構造器必需總是在構造過程失敗被觸發之前完成構造器代理。

如果超類的構造過程由于空的name值而失敗,整個構造過程程序立刻失敗,后續的構造過程代碼也不會執行。如果超類構造過程成功,CartItem構造器會驗證獲取到的quantity值是否大于等于1。

如果你用非空的name和大等于1的quantity創建一個CartItem實例,構造過程會成功:

if let twoSocks =CartItem(name:"sock", quantity:2) {

println("Item:\(twoSocks.name), quantity:\(twoSocks.quantity)")

}

// prints "Item: sock, quantity: 2"

如果你試著用值為0的quantity創建一個CartItem實例,構造器將會導致構造過程失敗:

if let zeroShirts =CartItem(name:"shirt", quantity:0) {

println("Item:\(zeroShirts.name), quantity:\(zeroShirts.quantity)")

}else{

println("Unable to initialize zero shirts")

}

// prints "Unable to initialize zero shirts"

同樣地,如果你嘗試用空的name值創建一個CartItem實例,超類Product構造器會導致構造過程失敗:

if let oneUnnamed =CartItem(name:"", quantity:1) {

println("Item:\(oneUnnamed.name), quantity:\(oneUnnamed.quantity)")

}else{

println("Unable to initialize one unnamed product")

}

// prints "Unable to initialize one unnamed product"

###可失敗構造器的重載

你可以在子類中重載超類的可失敗構造器,就像重載其他任意構造器那樣。另外,你還可以用子類的非可失敗構造器重載超類的可失敗構造器。這讓你能定義一個構造過程不能失敗的子類,盡管超類的構造過程是允許失敗的。

注意如果你用子類的非可失敗構造器重載了超類的可失敗構造器,子類構造器不能向上代理到超類構造器。非可失敗構造器庸官都不能代理給可失敗構造器。

注意:你可以用非可失敗構造器重載可失敗構造器,但是不能倒過來

下面的例子定義了一個叫做Document的類。這個類模擬了一個用name屬性初始化的文檔,name屬性是非空字符串或者是nil,但不能是空字符串:

class Document{

var name:String?

// this initializer creates a document with a nil name value

init() {}

// this initializer creates a document with a non-empty name value

init?(name:String) {

ifname.isEmpty {returnnil}

self.name = name

?}

}

下一個例子定義了一個Document的子類AutomaticallyNamedDocument。子類AutomaticallyNamedDocument重載了兩個Document添加的指定構造器。這些重載確保AutomaticallyNamedDocument實例在不用name初始化或傳入init(name:)的值是空字符串時將“[Untitled]”作為name的初值:

class AutomaticallyNamedDocument:Document{

override init() {

super.init()

self.name ="[Untitled]"

?}

override init(name:String) {

super.init()

if name.isEmpty {

self.name ="[Untitled]"

}else{

self.name = name

? }

?}

}

AutomaticallyNamedDocument用非可失敗的init(name:)構造器重載了它超類的可失敗的init?(name:)構造器。因為AutomaticallyNamedDocument應付空字符串情形的方式與它的超類不同,它的構造器不需要失敗,所以它提供了一個非可失敗版本的構造器來替代。

###init!可失敗構造器

你一貫地通過在init關鍵字后放置問號(init?)來定義一個創建可選值類型實例的可失敗構造器。另外,你還可以定義一個創建隱式解析可選類型實例的可失敗構造器。在init關鍵字后放置一個嘆號來替代問號(init!)就可以啦。

你可以從init?代理到init!,反之亦然;你可以用init!重載init?,反之亦然。你也可以從init代理到init!,不過這么做將會在init!發生構造過程失敗時觸發一個斷言。

##必需構造器(Required Initializers)

當你想讓一個類的某個構造器被所有子類都實現,你可以在定義這個構造器時在前面用required修飾:

class SomeClass{

required init() {

// initializer implementation goes here

?}

}

你也必須在它的子類定義必需構造器前用required修飾,但不必用override修飾:

class SomeSubClass:SomeClass{

requiredinit() {

// subclass implementation of the required initializer goes here

?}

}

這樣的語法可以將這種“必需”信號傳遞給未來更多的子類,表明這個構造器一定要實現,千秋萬代~

當然你可以滿足構造器的繼承規則來繼承必需構造器,這樣就不用“必須”重寫“必需”構造器了:

class SomeSubSub Class:SomeSubClass{

convenience init(a:String) {

self.init()

//subsubclass implementation of a convenience initializer goes here

?}

}

##通過閉包和函數來設置屬性的默認值

如果某個存儲型屬性的默認值需要特別的定制或準備,你就可以使用閉包或全局函數來為其屬性提供定制的默認值。每當某個屬性所屬的新類型實例創建時,對應的閉包或函數會被調用,而它們的返回值會當做默認值賦值給這個屬性。

class SomeClass{

let someProperty: SomeType = {

// 在這個閉包中給 someProperty 創建一個默認值

// someValue 必須和 SomeType 類型相同

return someValue

?}()

}

注意閉包結尾的大括號后面接了一對空的小括號。這是用來告訴 Swift 需要立刻執行此閉包。如果你忽略了這對括號,相當于是將閉包本身作為值賦值給了屬性,而不是將閉包的返回值賦值給屬性。

如果你使用閉包來初始化屬性的值,請記住在閉包執行時,實例的其它部分都還沒有初始化。這意味著你不能夠在閉包里訪問其它的屬性,就算這個屬性有默認值也不允許。同樣,你也不能使用隱式的self屬性,或者調用其它的實例方法。

原文鏈接:http://yulingtianxia.com/blog/2014/06/24/initialization-in-swift/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容