亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

Swift中文教程(十四) 初始化

系統(tǒng) 2034 0

初始化是類,結(jié)構(gòu)體和枚舉類型實例化的準備階段。這個階段設(shè)置這個實例存儲的屬性的初始化數(shù)值和做一些使用實例之前的準備以及必須要做的其他一些設(shè)置工作。

?

通過定義構(gòu)造器(initializers)實現(xiàn)這個實例化過程,也就是創(chuàng)建一個新的具體實例的特殊方法。和Objective-C不一樣的是,Swift的構(gòu)造器沒有返回值。它們主要充當?shù)慕巧谴_保這個實例在使用之前能正確的初始化。

?

類實例也能實現(xiàn)一個析構(gòu)器(deinitializer),在類實例銷毀之前做一些清理工作。更多的關(guān)于析構(gòu)器(deinitializer)的內(nèi)容可以參考Deinitialization。

?

?

1、存儲屬性的初始化
類和結(jié)構(gòu)體必須在它們被創(chuàng)建時把它們所有的屬性設(shè)置為合理的值。存儲屬性不能為不確定狀態(tài)

?

你可以在構(gòu)造方法里面給一個屬性設(shè)置一個初始值,或者在定義的時候給屬性設(shè)置一個默認值,這個行為將會在接下來的章節(jié)描述。


注意:當你對給一個屬性分配一個默認值的時候,它會調(diào)用它相對應(yīng)的初始化方法,這個值是對屬性直接設(shè)置的,不會通知它對應(yīng)的觀察者

?

構(gòu)造器
構(gòu)造器是創(chuàng)建一個具體類型實例的方法。最簡單的構(gòu)造器就是一個沒有任何參數(shù)實例方法,寫作init。

?

在下面的例子定義了一個叫Fahrenheit(華氏度)的新結(jié)構(gòu)體,來儲存轉(zhuǎn)換成華氏度的溫度。Fahrenheit結(jié)構(gòu)體,有一個屬性,叫temperature(溫度),它的類型為Double(雙精度浮點數(shù)):

      struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
println("The default temperature is \(f.temperature)° Fahrenheit")
// prints "The default temperature is 32.0° Fahrenheit"
    

?

這個結(jié)構(gòu)體定義了一個單一的構(gòu)造方法init,它沒有任何參數(shù),它儲存的溫度屬性初始化為32.0度。(水在華氏度的溫度情況下的冰點)。

?

屬性的默認值
如上所述,你可以在構(gòu)造器中設(shè)置它自己的儲存屬性的初始化值。或者在屬性聲明時,指定屬性的默認值,你指定一個默認的屬性值,會被分配到它定義的初始值。


注意:如果一個屬性常常使用同樣的初始化值 ,提供一個默認值會比在初始化使用一個默認值會更好。
同樣的結(jié)果,但是默認值與屬性的初始化在它定義地時候就緊緊地捆綁在一起。很簡單地就能構(gòu)造器更簡潔,和可以讓你從默認值中推斷出這個屬性的類型。默認值也能讓你優(yōu)化默認構(gòu)造器和繼承構(gòu)造器變得更容易,在本章會稍候描述。

?

你可以在上面的Fahrenheit(華氏度)結(jié)構(gòu)體定義時,給temperature(溫度)屬性提供默認值。

      struct Fahrenheit {
    var temperature = 32.0
}
    

?

2、自定義初始化(Customizing Initialization)
你可以根據(jù)輸入的參數(shù)來自定義初始化過程和可選的屬性類型,或者在初始化的時候修改靜態(tài)屬性。在這章節(jié)將會詳細敘述。

?

初始化參數(shù)
你可以在構(gòu)造器定義的時候提供一部分參數(shù),在自定義初始化過程中定義變量的類型和名稱。
初始化參和函數(shù)或者方法參數(shù)一樣有著同樣的功能。

?

在下面的例子中,定義了一個結(jié)構(gòu)體Celsius。儲存了轉(zhuǎn)換成攝氏度的溫度,Celsius結(jié)構(gòu)體實現(xiàn)了從不同的溫度初始化結(jié)構(gòu)體的兩個方法,init(fromFahrenheit:) 和init(fromKelvin:)。

      struct Celsius {
    var temperatureInCelsius: Double = 0.0
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
    

?

第一個構(gòu)造器只有一個初始化參數(shù),形參(External Parameter Names)fromFahrenheit,和實參(Local Parameter Names)fahrenheit。第二個構(gòu)造器有一個單一的初始化參數(shù),形參(External Parameter Names)fromKenvin,和實參(Local Parameter Names)kelvin。兩個構(gòu)造器都把單一的參數(shù)轉(zhuǎn)換為攝氏度和儲存到一個temperatureInCelsius的屬性.

?

實參名(Local Parameter Names)和形參名(External Parameter Names)
和函數(shù)參數(shù)和方法參數(shù)一樣,初始化參數(shù)擁有在構(gòu)造器函數(shù)體使用的實參,和在調(diào)用時使用的形參.
然而,和函數(shù)或者方法不同,構(gòu)造器在圓括號前面沒有一個識別函數(shù)名稱。因此,構(gòu)造器參數(shù)的名稱和類型,在被調(diào)用的時候,很大程度上扮演一個被識別的重要角 色。為此,在構(gòu)造器中,當你沒有提供形參名時,Swift就會為每一個參數(shù)提供一個自動的形參名。這個形參名和實參名相同,就像和之前你寫的每一個初始化 參數(shù)的hash符號一樣。


注意:如果你在構(gòu)造器中沒有定義形參,提供一個下橫線(_)作為區(qū)分形參和上面說描述的重寫默認行為。

?

在下面的例子 ,定義了一個結(jié)構(gòu)體Color,擁有三個靜態(tài)屬性red,green和blue。這些屬性儲存了從0.0到1.0的值,這些值代表紅色 ,綠色和藍色的深度。


Color提供了一個構(gòu)造器,以及三個雙精度(Double)類型的參數(shù):

      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
    }
}
    

?

無論什么時候,你創(chuàng)建一個Color實例,你必須使用每一個顏色的形參來調(diào)用構(gòu)造器:

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

?

值得注意的是,不能不通過形參名來調(diào)用構(gòu)造器。在構(gòu)造器定義之后,形參名必須一致使用。如果漏掉就會在編寫時提示錯誤。

      let veryGreen = Color(0.0, 1.0, 0.0)
// this reports a compile-time error - external names are required
    

?

可選類型
如果你儲存屬性使用的是自定義的類型在邏輯上允許值為空-或者他們的值并不在構(gòu)造器中初始化,或者他們被允許為空。可以定義一個可選類型的屬性。可選類型屬性是一個自動初始化值為nil,表示這個屬性有意在構(gòu)造器中設(shè)置為“空值”(no value yet)。


在下面的例子中,定義了一個SurveryQuestion類,擁有一個可選的String屬性response。

?

這個回答在他們調(diào)查問題在發(fā)布之前是無法知道的,所以response定義為類型String? ,或者叫可選String(optional String)。說明它會被自動分配一個默認值nil,意思為當surverQuestion初始化時還不存在。

?

在初始化時修改靜態(tài)屬性
當你在設(shè)置靜態(tài)屬性值時,只要在初始化完成之前,你都可以在初始化時隨時修改靜態(tài)屬性。
注意:對于類的實例化,一個靜態(tài)屬性只能在初始化時被修改,這個初始化在類定義時已經(jīng)確定。

?

你可以重寫SurveryQuestion例子,對于問題的text屬性,使用靜態(tài)屬性會比動態(tài)屬性要好,因為SurveyQuestion實例被創(chuàng)建之后就無法修改。盡管text屬性現(xiàn)在是靜態(tài)的,但是仍然可以在構(gòu)造器中被設(shè)置:

      class SurveyQuestion {
    let text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        println(text)
    }
}
let beetsQuestion = SurveyQuestion(text: "How about beets?")
beetsQuestion.ask()
// prints "How about beets?"
beetsQuestion.response = "I also like beets. (But not with cheese.)"
    

?

3、默認構(gòu)造器

?

Swift為每一個結(jié)構(gòu)或者基類提供了默認的構(gòu)造器,來初始化它們所包含的所有屬性。默認構(gòu)造器將會創(chuàng)建一個新的實例然后將它們的屬性設(shè)置為默認值。

?

下面的例子定義了一個叫ShoppingListItem的類,包含了名稱,數(shù)量和是否已購買的屬性,將會被用在購物清單中:

      class ShoppingListItem {
    var name: String?
    var quantity = 1
    var purchased = false
}
var item = ShoppingListItem()
    

?

因為ShoppingListItem類中所有的屬性都有默認值,并且這個類是一個沒有父類的基類,所以它默認擁有一個會將所有包含的屬性設(shè)置為初 始值的默認構(gòu)造器。比如在這個例子中name屬性是一個可選String屬性,它會被默認設(shè)置為nil,盡管在代碼中沒有指明。上面的例子使用默認構(gòu)造器 創(chuàng)建了一個ShoppingListItem類,記做ShoppingListItem(),然后將它賦值給了變量item。

?

結(jié)構(gòu)類型的成員逐一構(gòu)造器

?

除了上面提到的默認構(gòu)造器之外,結(jié)構(gòu)類型還有另外一種成員逐一完成初始化的構(gòu)造器,可以在定義結(jié)構(gòu)的時候直接指定每個屬性的初始值。

?

成員逐一構(gòu)造器是一種為結(jié)構(gòu)的成員屬性進行初始化的簡便方法。下面的例子定義了一個叫Size的結(jié)構(gòu),和兩個屬性分別叫width和height。每個屬性都是Double類型的并且被初始化為0.0。

?

因為每個存儲屬性都有默認值,在Size結(jié)構(gòu)創(chuàng)建一個實例的時候就可以自動調(diào)用這個成員逐一構(gòu)造器init(width:height:):

      struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
    

?

4、數(shù)值類型的構(gòu)造器代理

?

在實例的初始化過程中,構(gòu)造器可以調(diào)用其他的構(gòu)造器來完成初始化。這個過程叫構(gòu)造器代理,可以避免多個構(gòu)造器的重復代碼。

?

對于數(shù)值類型和類來說,構(gòu)造器代理的工作形式是不一樣的。數(shù)值類型(結(jié)構(gòu)和枚舉)不支持繼承,因此他們的構(gòu)造器代理相對簡單,因為它們只能使用自己 的構(gòu)造器代理。但是一個類可以繼承自另外一個類,所以類需要確保在初始化的時候?qū)⑺械拇鎯傩远荚O(shè)置為正確的值。這種過程在下一節(jié)類的繼承和初始化中 敘述。

?

對于數(shù)值類型來說,可以使用self.init來調(diào)用其他構(gòu)造器,注意只能在這個數(shù)值類型內(nèi)部調(diào)用相應(yīng)的構(gòu)造器。

?

需要注意的是如果你為數(shù)值類型定義了一個構(gòu)造器,你就不能再使用默認構(gòu)造器了。這種特性可以避免當你提供了一個特別復雜的構(gòu)造器的時候,另外一個人誤使用了默認構(gòu)造器而出錯。

?

注意:如果你想要同時使用默認構(gòu)造器和你自己設(shè)置的構(gòu)造器,不要將這兩種構(gòu)造器寫在一起,而是使用擴展形式。更多內(nèi)容可以參考Extensions一章。

?

下面的示例定義了一個結(jié)構(gòu)Rect來表示一個幾何中的矩形。這個Rect結(jié)構(gòu)需要另外兩個結(jié)構(gòu)來組成,包括Size和Point,初始值均為0.0:

      struct Size {
    var width = 0.0, height = 0.0
}
struct Point {
    var x = 0.0, y = 0.0
}
    

?

現(xiàn)在你有三種初始化Rect結(jié)構(gòu)的方式:直接使用為origin和size屬性初始化的0值,給定一個指定的origin和size,或者使用中心點和大小來初始化。下面的例子包含了這三種初始化方式:

      struct Rect {
    var origin = Point()
    var size = 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()構(gòu)造器和默認構(gòu)造器的功能相同。這個構(gòu)造器不需要任何內(nèi)容,只是用來在已有其他構(gòu)造器的時候表示默認構(gòu)造器的依然存在。調(diào)用這個構(gòu)造器 創(chuàng)建的Rect,根據(jù)Point和Size的結(jié)構(gòu)定義,Point(x: 0.0, y: 0.0) ,Size(width: 0.0, height: 0.0) origin和size都會被設(shè)置為0。

      let basicRect = Rect()
// basicRect's origin is (0.0, 0.0) and its size is (0.0, 0.0)
    

?

第二個Rect構(gòu)造器init(origin:size:)和成員逐一構(gòu)造器類似,它使用給定的值來初始化結(jié)構(gòu)的屬性:

      let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
    size: Size(width: 5.0, height: 5.0))
// originRect's origin is (2.0, 2.0) and its size is (5.0, 5.0)
    

?

第三個構(gòu)造器init(center:size)就更加復雜一些,它首先使用center和size計算出了origin的值,然后調(diào)用(或者是使用代理)了init(origin:size)構(gòu)造器,設(shè)置origin和size的值:

      let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
    size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
    

?

init(center:size:)構(gòu)造器同樣可以設(shè)置oringin和size的值,而且使用起來也非常方便,代碼也比較簡潔因為它使用了已有的一些構(gòu)造器。

?

注意:可以參考Extensions一章,學習怎樣省略init()和init(origin:size:)

?

5、類的繼承和初始化

?

譯者注:本小節(jié)內(nèi)容Apple從底層解釋,十分復雜,建議有需要的讀者自行閱讀英文原文。

?

本小節(jié)主要的意思就是說:

?

1、自定義初始化方法要先調(diào)用自己類默認初始化方法,自己重寫默認初始化方法要先調(diào)用父類默認初始化方法

?

2、應(yīng)該要先調(diào)用父類的構(gòu)造器或者自身的默認構(gòu)造器,以防止先給屬性賦值了然后才調(diào)用父類或者自身的默認構(gòu)造器把以前的賦值覆蓋了

?

一個類的所有存儲屬性-包括從父類繼承而來的屬性-都必須在初始化的時候設(shè)置初始值。

?

Swift為class類型定義了兩種構(gòu)造器來確保它們所有的存儲屬性都設(shè)置了初始值。這兩種方式叫做指定構(gòu)造器和便捷構(gòu)造器。

?

指定構(gòu)造器和便捷構(gòu)造器

?

指定構(gòu)造器是一個類最主要的構(gòu)造器。指定構(gòu)造器通過設(shè)置所有屬性的初值并且調(diào)用所有的父類構(gòu)造器來根據(jù)構(gòu)造鏈一次初始化所有的屬性。

?

類所擁有的指定構(gòu)造器很少,一般只有一個,并且是連接這父類的構(gòu)造鏈依次完成構(gòu)造的。

?

每個類至少有一個指定構(gòu)造器,在有些情況下,需要使用繼承來從父類中得到該指定構(gòu)造器,更多內(nèi)容可以查看后面的Automatic Initializer Inheritance章節(jié)。

?

便捷構(gòu)造器是類的第二種常用構(gòu)造器。你可以調(diào)用同一個類中的指定構(gòu)造器來定義一個便捷構(gòu)造器,使用指定構(gòu)造器來設(shè)置相關(guān)的參數(shù)默認值。你還可以定義一個便捷構(gòu)造器來創(chuàng)建這個類的實例或者是別的特殊用途。

?

如果你的類不需要它們,也可以不定義便捷構(gòu)造器。不過對于常見初始化模型需要快捷方式的時候創(chuàng)建一個便捷構(gòu)造器可以讓你的初始化過程變成十分簡單便捷。

?

構(gòu)造鏈

?

為了簡化指定構(gòu)造器和便捷構(gòu)造器的關(guān)系,Swift為兩種構(gòu)造器的代理調(diào)用設(shè)置了三個規(guī)則:

?

規(guī)則1

?

指定構(gòu)造器必須調(diào)用它直接父類的指定構(gòu)造器

?

規(guī)則2

?

便捷構(gòu)造器只能調(diào)用同一個類中的其它構(gòu)造器

?

規(guī)則3

?

便捷構(gòu)造器必須以調(diào)用一個指定構(gòu)造器結(jié)束

?

記下這些規(guī)則的簡單方法是:

?

指定構(gòu)造器必須向上代理

?

便捷構(gòu)造器必須橫向代理

?

可以使用下面的圖來表示:

?

image

?

父類中的兩個便捷構(gòu)造器依次調(diào)用直到指定構(gòu)造器,子類中的指定構(gòu)造器調(diào)用了父類的指定構(gòu)造器。

?

注意:這些規(guī)則不會影響每個類的實例創(chuàng)建過程。每個構(gòu)造器都可以用來創(chuàng)建它們各自的類的實例。這些規(guī)則只影響你如何編寫類實現(xiàn)代碼。

?

下圖演示的是另一種更為復雜的具有四個等級的類。這個圖展示了指定構(gòu)造器在類的初始化過程中如何被作為“漏斗”節(jié)點的。這個構(gòu)造鏈簡化了類與類之間的交互關(guān)系:

?

image

?

兩階段的初始化

?

在Swift中,類的初始化要經(jīng)過兩個階段。在第一個階段,每一個存儲屬性都被設(shè)置了一個初始值。一旦每個存儲屬性的值在初始化階段被設(shè)置了,在第二個階段,每個類在這個實例被使用之前都會有機會來設(shè)置它們相應(yīng)的存儲屬性。

?

兩階段的模式使初始化過程更加安全,還可以讓每個類在類的層級關(guān)系中具有更多的可能性。兩階段初始化方法可以防止屬性在被初始化之前就被使用,或者是被另一個構(gòu)造器錯誤地賦值。

?

注意:Swift的這種兩階段初始化方法跟Objective-C中的類似。主要的差別是在第一個過程中,Objective-C為每個屬性賦值0或者null,而在Swift中,可以個性化設(shè)置這些初始值,還可以處理一些初始值不能是0或者nil的情況。

?

Swift編譯器通過四重檢查來確保兩階段式的初始化過程是完全正確無誤的:

?

Safety check 1
A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

?

As mentioned above, the memory for an object is only considered fully initialized once the initial state of all of its stored properties is known. In order for this rule to be satisfied, a designated initializer must make sure that all its own properties are initialized before it hands off up the chain.

?

Safety check 2
A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.
Safety check 3
A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.
Safety check 4
An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete.

?

The class instance is not fully valid until the first phase ends. Properties can only be accessed, and methods can only be called, once the class instance is known to be valid at the end of the first phase.

?

Here’s how two-phase initialization plays out, based on the four safety checks above:

?

Phase 1

?

  • A designated or convenience initializer is called on a class.
  • Memory for a new instance of that class is allocated. The memory is not yet initialized.
  • A designated initializer for that class confirms that all stored properties introduced by that class have a value. The memory for these stored properties is now initialized.
  • The designated initializer hands off to a superclass initializer to perform the same task for its own stored properties.
  • This continues up the class inheritance chain until the top of the chain is reached.
  • Once the top of the chain is reached, and the final class in the chain has ensured that all of its stored properties have a value, the instance’s memory is considered to be fully initialized, and phase 1 is complete.

Phase 2

?

  • Working back down from the top of the chain, each designated initializer in the chain has the option to customize the instance further. Initializers are now able to access self and can modify its properties, call its instance methods, and so on.
  • Finally, any convenience initializers in the chain have the option to customize the instance and to work with self .

Here’s how phase 1 looks for an initialization call for a hypothetical subclass and superclass:

?

image

?

In this example, initialization begins with a call to a convenience initializer on the subclass. This convenience initializer cannot yet modify any properties. It delegates across to a designated initializer from the same class.

?

The designated initializer makes sure that all of the subclass’s properties have a value, as per safety check 1. It then calls a designated initializer on its superclass to continue the initialization up the chain.

?

The superclass’s designated initializer makes sure that all of the superclass properties have a value. There are no further superclasses to initialize, and so no further delegation is needed.

?

As soon as all properties of the superclass have an initial value, its memory is considered fully initialized, and Phase 1 is complete.

?

Here’s how phase 2 looks for the same initialization call:

?

image

?

The superclass’s designated initializer now has an opportunity to customize the instance further (although it does not have to).

?

Once the superclass’s designated initializer is finished, the subclass’s designated initializer can perform additional customization (although again, it does not have to).

?

Finally, once the subclass’s designated initializer is finished, the convenience initializer that was originally called can perform additional customization.

?

構(gòu)造器的繼承和重寫

?

Unlike subclasses in Objective-C, Swift subclasses do not not inherit their superclass initializers by default. Swift’s approach prevents a situation in which a simple initializer from a superclass is automatically inherited by a more specialized subclass and is used to create a new instance of the subclass that is not fully or correctly initialized.

?

If you want your custom subclass to present one or more of the same initializers as its superclass—perhaps to perform some customization during initialization—you can provide an overriding implementation of the same initializer within your custom subclass.

?

If the initializer you are overriding is a designated initializer, you can override its implementation in your subclass and call the superclass version of the initializer from within your overriding version.

?

If the initializer you are overriding is a convenience initializer, your override must call another designated initializer from its own subclass, as per the rules described above in Initializer Chaining .

?

?

NOTE

?

Unlike methods, properties, and subscripts, you do not need to write the override keyword when overriding an initializer.

?

構(gòu)造器自動繼承

?

As mentioned above, subclasses do not not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met. In practice, this means that you do not need to write initializer overrides in many common scenarios, and can inherit your superclass initializers with minimal effort whenever it is safe to do so.

?

Assuming that you provide default values for any new properties you introduce in a subclass, the following two rules apply:

?

Rule 1
If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
Rule 2
If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

These rules apply even if your subclass adds further convenience initializers.

?

?

NOTE

?

A subclass can implement a superclass designated initializer as a subclass convenience initializer as part of satisfying rule 2.

?

指定初始化和便捷初始化的語法

?

Designated initializers for classes are written in the same way as simple initializers for value types:

      init(parameters) {
    statements
}
    

?

Convenience initializers are written in the same style, but with the convenience keyword placed before the init keyword, separated by a space:

      convenience init(parameters) {
    statements
}
    

?

指定初始化和便捷初始化實戰(zhàn)

?

下面的例子演示的是指定構(gòu)造器,便捷構(gòu)造器和自動構(gòu)造器繼承的實戰(zhàn)。例子中定義了三個類分別叫Food,RecipeIngredient和ShoppingListItem,并給出了他們的繼承關(guān)系。

?

基類叫做Food,是一個簡單的類只有一個name屬性:

      class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}
    

?

下圖就是Food類的構(gòu)造鏈:

?

image

?

類不存在成員逐一構(gòu)造器,所以Food類提供了一個指定構(gòu)造器,使用參數(shù)name來完成初始化:

      let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"
    

?

init(name:String)構(gòu)造器就是Food類中的指定構(gòu)造器,因為它保證了每一個Food實例的屬性都被初始化了。由于它沒有父類,所以不需要調(diào)用super.init()構(gòu)造器。

?

Food類也提供了便捷構(gòu)造器init(),這個構(gòu)造器沒有參數(shù),僅僅只是將name設(shè)置為了[Unnamed]:

      let mysteryMeat = Food()
// mysteryMeat's name is "[Unnamed]"
    

?

下一個類是Food的子類,叫做RecipeIngredient。這個類描述的是做飯時候的配料,包括一個數(shù)量屬性Int類型,然后定義了兩個構(gòu)造器:

      class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}
    

?

下圖表示這兩個類的構(gòu)造鏈:

?

image RecipeIngredient類有它自己的指定構(gòu)造器init(name: String, quantity:Int),用來創(chuàng)建一個新的RecipeIngredient實例。在這個指定構(gòu)造器中它調(diào)用了父類的指定構(gòu)造器init(name:String)。

?

然后它還有一個便捷構(gòu)造器,init(name),它使用了同一個類中的指定構(gòu)造器。當然它還包括一個繼承來的默認構(gòu)造器init(),這個構(gòu)造器將使用RecipeIngredient中的init(name: String)構(gòu)造器。

?

RecipeIngredient also defines a convenience initializer, init(name: String) , which is used to create a RecipeIngredient instance by name alone. This convenience initializer assumes a quantity of 1 for any RecipeIngredient instance that is created without an explicit quantity. The definition of this convenience initializer makes RecipeIngredient instances quicker and more convenient to create, and avoids code duplication when creating several single-quantity RecipeIngredient instances. This convenience initializer simply delegates across to the class’s designated initializer.

?

Note that the init(name: String) convenience initializer provided by RecipeIngredient takes the same parameters as the init(name: String) designated initializer from Food . Even though RecipeIngredient provides this initializer as a convenience initializer, RecipeIngredient has nonetheless provided an implementation of all of its superclass’s designated initializers. Therefore, RecipeIngredient automatically inherits all of its superclass’s convenience initializers too.

?

In this example, the superclass for RecipeIngredient is Food , which has a single convenience initializer called init() . This initializer is therefore inherited by RecipeIngredient . The inherited version of init() functions in exactly the same way as the Food version, except that it delegates to the RecipeIngredient version of init(name: String) rather than the Food version.

?

上述三種構(gòu)造器都可以用來創(chuàng)建RecipeIngredient實例:

      let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)
    

?

最后一個類是ShoppingListItem繼承自RecipeIngredient,它又包括了另外兩個屬性,是否已購買purchased,描述description,描述本身還是一個計算屬性:

      class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
    var output = "\(quantity) x \(name.lowercaseString)"
        output += purchased ? " yes" : " no"
        return output
    }
}
    

?

注意:ShoppingListItem沒有定義構(gòu)造器來初始化purchased的值,因為每個商品在買之前purchased都是默認被設(shè)置為沒有被購買的。

?

因為ShoppingListItem沒有提供其他構(gòu)造器,那么它就完全繼承了父類的構(gòu)造器,用下圖可以說明:

?

image

你可以在創(chuàng)建ShoppingListItem實例時使用所有的繼承構(gòu)造器:

      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 yes
// 1 x bacon no
// 6 x eggs no
    

?

通過輸出可以看出所有的實例在創(chuàng)建的時候,屬性的默認值都被正確的初始化了。

?

6、通過閉包或者函數(shù)來設(shè)置一個默認屬性值

?

如果存儲屬性的默認值需要額外的特殊設(shè)置,可以使用閉包或者函數(shù)來完成。

?

閉包或者函數(shù)會創(chuàng)建一個臨時變量來作為返回值為這個屬性賦值。下面是如果使用閉包賦值的一個示意代碼:

      class SomeClass {
    let someProperty: SomeType = {
        // create a default value for someProperty inside this closure
        // someValue must be of the same type as SomeType
        return someValue
        }()
}
    

?

需要注意的是在閉包結(jié)尾有兩個小括號,告訴Swift這個閉包是需要立即執(zhí)行的。

?

注意:如果你時候閉包來初始化一個屬性,在閉包執(zhí)行的時候,后續(xù)的一些屬性還沒有被初始化。在閉包中不要訪問任何后面的屬性,一面發(fā)生錯誤,也不能使用self屬性,或者其它實例方法。

?

下面的例子是一個叫Checkerboard的結(jié)構(gòu),是由游戲Checkers來的

?

image 這 個游戲是在一個10×10的黑白相間的格子上進行的。來表示這個游戲盤,使用了一個叫Checkerboard的結(jié)構(gòu),其中一個屬性叫 boardColors,是一個100個Bool類型的數(shù)組。true表示這個格子是黑色,false表示是白色。那么在初始化的時候可以通過下面的代碼 來初始化:

      struct Checkerboard {
    let boardColors: Bool[] = {
        var temporaryBoard = Bool[]()
        var isBlack = false
        for i in 1...10 {
            for j in 1...10 {
                temporaryBoard.append(isBlack)
                isBlack = !isBlack
            }
            isBlack = !isBlack
        }
        return temporaryBoard
        }()
    func squareIsBlackAtRow(row: Int, column: Int) -> Bool {
        return boardColors[(row * 10) + column]
    }
}
    

??

當一個新的Checkerboard實例創(chuàng)建的時候,閉包會執(zhí)行,然后boardColor的默認值將會被依次計算并且返回,然后作為結(jié)構(gòu)的一個屬性。通過使用squareIsBlackAtRow工具函數(shù)可以檢測是否被正確設(shè)置:

      let board = Checkerboard()
println(board.squareIsBlackAtRow(0, column: 1))
// prints "true"
println(board.squareIsBlackAtRow(9, column: 9))
// prints "false"?
    

?

感謝翻譯小組成員:李起攀( 微博 )、若晨( 微博 )、YAO、粽子、山有木兮木有枝、渺-Bessie、墨離、矮人王、CXH、Tiger大顧( 微博 )


原始鏈接 http://letsswift.com/2014/06/initialization

?

?

Swift中文教程(十四) 初始化


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产精品深夜福利免费观看 | 在线观看年轻的母亲 | 97视频在线免费观看 | 福利视频国产 | 99热视| 欧美一级视频在线观看欧美 | 狠狠操女人| 天天做人人爱夜夜爽2020毛片 | 99热这里只有精品66 | 成人窝窝午夜看片 | 久操视频在线观看免费 | 欧美性猛交xxxx免费看久久 | 国产精品久久久久影视不卡 | 精品夜夜春夜夜爽久久 | 亚洲免费在线视频 | 精品人人做人人爽久久久 | 国产精品久久久久激情影院 | 日日操天天操 | 亚洲精品一二三四 | 亚洲国产成人精品一区91 | 久久免费看片 | 色老头福影院韩国激情影院 | 色婷婷综合在线 | 综合久色| 日本高清二区 | 国内夫妇精品对白在线播放 | 欧美曰批人成在线观看 | 波多野结衣乳巨码无在线观看 | 特级毛片免费视频播放 | 处videossex第一次中 | 久久专区 | 国产一区二区免费在线观看 | 中文一区在线观看 | 亚洲成人福利 | 男女午夜激情 | 亚洲欧洲高清 | 色婷婷婷丁香亚洲综合不卡 | 国产激情视频一区二区三区 | 哪里有毛片 | 26uuu另类亚洲欧美日本一 | 国产成人久久精品二区三区 |