開篇
聲明一個(gè)類、結(jié)構(gòu)體或枚舉,就像繪制了一副草圖,即使描繪得再栩栩如生,也僅躍于紙上罷了。
舉例來(lái)說(shuō),描述一輛汽車:車型、車身顏色、出廠日期等等。為此聲明一個(gè) Car 的類:
class Car{
var name:String /**> 車輛型號(hào) */
var color:String /**> 車身顏色 */
var date:NSDate /**> 出廠日期 */
// 當(dāng)然還有其他屬性
}
此時(shí)手上只有Car的設(shè)計(jì)圖,這并不意味著你已經(jīng)擁有一輛保時(shí)捷或法拉利可以去兜風(fēng)了;所以嘍,還是將設(shè)計(jì)稿圖交給工廠,由他們按照設(shè)計(jì)圖制造一輛貨真價(jià)實(shí)的小汽車交付給你。ps:制造出來(lái)的車在編程中叫實(shí)例(顧名思義:實(shí)際的例子)。
等等,工廠拒絕了!拒絕理由:車型沒有指明,噴什么顏色呢?
修改如下:
class Car{
var name:String = "保時(shí)捷911" /**> 車輛型號(hào) */
var color:String = "紅色" /**> 車身顏色 */
var date:NSDate = NSDate() /**> 出廠日期 */
// 當(dāng)然還有其他屬性
}
ok!默認(rèn)車型“保時(shí)捷”、車身顏色“紅色”,出廠日期為加工當(dāng)天。
獨(dú)樂樂不如眾樂樂,于是大手一揮,再加工一打!
廠家:還是全紅色的保時(shí)捷911?
當(dāng)初想得過于簡(jiǎn)單,設(shè)計(jì)稿圖參數(shù)全給了默認(rèn)值,失策失策!
修改如下:
class Car{
var name:String /**> 車輛型號(hào) */
var color:String /**> 車身顏色 */
var date:NSDate /**> 出廠日期 */
// 當(dāng)然還有其他屬性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
至此,告訴廠家:第一輛要藍(lán)色的保時(shí)捷911,第二輛要紅色的保時(shí)捷Cayenne,第三輛...
幾種類型的Initialization
枚舉:
enum Direction{
case East, South, West, North
init(symbol:Character){
switch symbol{
case "E":
self = .East
case "S":
self = .South
case "W":
self = .West
case "N":
self = .North
default:
self = .East
}
}
}
let dir = Direction(symbol: "S") // .South
結(jié)構(gòu)體1:
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
let temp = Fahrenheit()
結(jié)構(gòu)體2:
struct User {
var username:String
var password:String
}
// 盡管沒有聲明初始化方法 但是對(duì)于結(jié)構(gòu)體默認(rèn)是有memberwise initialization
let user = User(username: "test", password: "123456")
類:
// 類初始化
class Car{
var name:String
var color:String
var age:Int
init(name:String,color:String,age:Int){
self.name = name
self.color = color
self.age = age
}
}
何為Initialization
根據(jù)設(shè)計(jì)稿圖制造出車輛實(shí)例,在車輛出廠使用前,必須確認(rèn)車輛車型和為車身噴漆,這是一個(gè) Initialization 過程,保證車輛實(shí)例使用前完成所有的準(zhǔn)備工作。
官方文檔對(duì)此的描述是:
This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
關(guān)鍵詞:stored property存儲(chǔ)屬性,換句話說(shuō)對(duì)于computed property計(jì)算屬性是排除在外的。
再來(lái)說(shuō)說(shuō) Swift 中 Initialization 的聲明形式,使用init修飾符即可:
init() {
// perform some initialization here
}
默認(rèn)值
倘若你偏愛紅色的保時(shí)捷,那么你可以一開始就為實(shí)例屬性color設(shè)定默認(rèn)屬性為“紅色”
class Car{
// ...
var color:String = "紅色" /**> 車身顏色 */
// ...
}
自定義
倘若你天馬星空,那么你就應(yīng)該聲明一個(gè)自定義的構(gòu)造方法,傳入配色和車型來(lái)實(shí)例化車輛。
class Car{
var name:String /**> 車輛型號(hào) */
var color:String /**> 車身顏色 */
var date:NSDate /**> 出廠日期 */
// 當(dāng)然還有其他屬性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
局部和外部參數(shù)名
當(dāng)然就像函數(shù)(function)和方法(method)一樣,構(gòu)造方法也是可以擁有l(wèi)ocal name 和 external name。
Swift 自動(dòng)為每一個(gè)參數(shù)提供 external parameter 名字,換句話說(shuō)你丫別提供了??!
來(lái)自官方文檔的例子:
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red //這里的red green blue 是局部參數(shù)名
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// 這里的red green blue 是外部參數(shù)名字
// 這個(gè)是很有必要的,否則調(diào)用者怎么知道我傳入的1.0 0.0 1.0 到底賦值給哪個(gè)參數(shù)了呢?。?!
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
let halfGray = Color(white: 0.5)
注意到red、blue和green 即使local parameters,能夠在init中使用;又可以在構(gòu)造方法調(diào)用時(shí)作為external parameters 呈現(xiàn)
總有些“搗蛋鬼”就是想反其道而行:不要外部標(biāo)簽可以嗎?? 好吧,答案自然是ok。使用** _ **下劃線占據(jù)external parameter name的位置,告知編譯器在調(diào)用構(gòu)造方法時(shí)忽略外部參數(shù)名:
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
實(shí)例變量是可選參數(shù)類型
說(shuō)說(shuō)車輛的牌照,車輛剛制造出來(lái)時(shí),對(duì)于車牌沒有強(qiáng)制要求,一個(gè)月后上牌就OK。所以嘍,我們可以為Car設(shè)定一個(gè)可選類型的車牌實(shí)例變量:
class Car{
var licensePlate:String? /**> 因?yàn)檐嚺埔婚_始可以是沒有的
var name:String /**> 車輛型號(hào) */
var color:String /**> 車身顏色 */
var date:NSDate /**> 出廠日期 */
// 當(dāng)然還有其他屬性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
let car = Car(name:"保時(shí)捷911",color:"紅色")
// 搖到號(hào)了
car.licensePlate = "浙A123456"
常量屬性
讓我們?cè)凇坝?jì)較”點(diǎn),車輛車型選定后,之后是絕無(wú)修改的可能,要知道改裝車輛是違法的,所以嘍,將name 聲明為常量是一個(gè)明智的選擇。
class Car{
var licensePlate:String? /**> 因?yàn)檐嚺埔婚_始可以是沒有的
let name:String /**> 車輛型號(hào) */
var color:String /**> 車身顏色 */
var date:NSDate /**> 出廠日期 */
// 當(dāng)然還有其他屬性
init(name:String,color:String){
self.name = name
self.color = color
date = NSDate()
}
}
默認(rèn)的 Initializers
Swift 為以下對(duì)象提供默認(rèn)構(gòu)造方法:
- Structure(結(jié)構(gòu)體)
- 所有實(shí)例屬性均設(shè)置了默認(rèn)值的Class(類)
以官方文檔為例:
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
對(duì)于類來(lái)說(shuō),我們并未寫一個(gè)init方法,而是給定每一個(gè)實(shí)例屬性默認(rèn)值。
結(jié)構(gòu)體有點(diǎn)特殊,無(wú)須為實(shí)例屬性設(shè)定默認(rèn)值,先看看結(jié)構(gòu)體的聲明:
struct StructTest{
// memberwise 逐個(gè)成員初始化唄
var para1:String
var para2:String
var para3:String
var para4:String
}
注意我們并未給任何一個(gè)參數(shù)設(shè)定初始值,但實(shí)際上結(jié)構(gòu)體已經(jīng)就自定 memberwise initializer了(memberwise:逐個(gè)成員的意思),即生成了init(para1:,para2:,para3:,para4:)構(gòu)造方法了!
值類型中的 Initializer Delegation
對(duì)于值類型(結(jié)構(gòu)體和枚舉)來(lái)說(shuō),是沒有繼承的,這意味著只允許構(gòu)造方法之間的互相調(diào)用,即使用self.init,而沒有super.init一說(shuō)。
再次提醒,構(gòu)造方法的作用就是確保實(shí)例在使用前,其所有實(shí)例屬性都設(shè)定了初始值!
舉例:
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
// 1
init() {}
// 2
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
// 3
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
// 至于這里調(diào)用self.init() 無(wú)非就是不想在寫self.origin = origin和self.size = size
// 由于這里初始化代碼簡(jiǎn)單,所以作用感覺不出,但是某些情況下確實(shí)可以節(jié)省代碼量
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
1、2 和 3 都是構(gòu)造方法,其中1中,由于我們?yōu)榻Y(jié)構(gòu)體中的所有成員都給定了默認(rèn)值,因?yàn)?code>init(){}方法中什么都沒寫,但是倘若我們未給結(jié)構(gòu)體成員默認(rèn)值,那么init(){}報(bào)錯(cuò)Return from initializer without all stored properties 很好理解,構(gòu)造方法的職責(zé)就是為所有實(shí)例變量在使用前設(shè)定初始值!假若你沒有那么做,自然報(bào)錯(cuò)嘍。