構(gòu)造過程是為了使用某個類、結(jié)構(gòu)體饼齿、或枚舉類型的實例而進行準(zhǔn)備的過程饲漾。整個過程包含了為實例中的每個屬性設(shè)置初始值和為其其執(zhí)行必要的準(zhǔn)備和初始化任務(wù)。
Swift 構(gòu)造函數(shù)使用 init()
與Objective-C中的構(gòu)造器不同缕溉,Swift的構(gòu)造器無需返回值考传,他們的主要任務(wù)是保證新shili在第一次使用前完成正確的初始化。類實例也可以通過定義析構(gòu)器(deinitializer)是類實例釋放之前執(zhí)行清理內(nèi)存的工作证鸥。
存儲型屬性的初始賦值
類和結(jié)構(gòu)體在實例創(chuàng)建時僚楞,必須為所有存儲型屬性設(shè)置合適的初始值。
存儲屬性在構(gòu)造器中賦值時枉层,它們的值時被直接設(shè)置的泉褐,不會觸發(fā)任何屬性觀察器。
存儲屬性在構(gòu)造器中賦值流程:
- 創(chuàng)建初始值鸟蜡。
- 在屬性定義中指定默認(rèn)屬性值膜赃。
- 初始化實例,并調(diào)用init()方法揉忘。
構(gòu)造器
構(gòu)造器在創(chuàng)建某特定類的新實例時調(diào)用跳座。它的最簡單形式類似于一個不帶任何參數(shù)的實例方法,以關(guān)鍵字 init命名癌淮。
語法
init() {
//實例化后執(zhí)行的代碼
}
語法
以下結(jié)構(gòu)體定義了一個不帶參數(shù)的構(gòu)造器 init,并在里面講存儲型屬性 length 和 breadth 的初始化為 6 和 12:
struct rectangle {
var length: Double
var breadth: Double
init() {
length = 6
breadth = 12
}
}
var area = rectangle()
print("矩形面積為 \(area.length*area.breadth)")
矩形面積為 72.0
默認(rèn)屬性值
我們可以在構(gòu)造器中為存儲型屬性設(shè)置初始值躺坟;同樣也可以在屬性聲明時為其設(shè)置默認(rèn)值沦补。使用默認(rèn)值能然你的構(gòu)造器更簡介乳蓄、更清晰、且能通過默認(rèn)值自動推導(dǎo)出屬性的類型夕膀。
以下實例我們在屬性聲明時為其設(shè)置默認(rèn)值:
struct rectangle {
// 設(shè)置默認(rèn)值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面積為 \(area.length*area.breadth)")
矩形面積為 72.0
構(gòu)造參數(shù)
你可以在定義器 init() 時提供構(gòu)造參數(shù)虚倒,如下所示:
struct Rectangle {
var length: Double
var breadth: Double
var area: Double
init(fromLength length: Double, fromBreadth breadth: Double) {
self.length = length
self.breadth = breadth
area = length * breadth
}
init(fromLeng leng: Double, fromBread bread: Double) {
self.length = leng
self.breadth = bread
area = leng * bread
}
let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("面積為: \(ar.area)")
let are = Rectangle(fromLeng: 36, fromBread: 12)
print("面積為: \(are.area)")
面積為: 72.0
面積為: 432.0
內(nèi)部參數(shù)和外部參數(shù)
跟函數(shù)和方法參數(shù)相同美侦,構(gòu)造器也存在一個在構(gòu)造器內(nèi)部使用的參數(shù)名字和一個在調(diào)用構(gòu)造器時使用的外部參數(shù)名字。然而魂奥,構(gòu)造器并不像函數(shù)和方法那樣在括號前有個可辨別的名字菠剩,所以在調(diào)用構(gòu)造器時,主要通過構(gòu)造器中的參數(shù)名和類型來確定需要調(diào)用的構(gòu)造器耻煤。
如果你咋已定義構(gòu)造器時沒有提供參數(shù)的外部名字具壮,Swift會為每個構(gòu)造器自動生成一個跟內(nèi)部名字相同的外部名。
struct Color {
let red, green, blue: Double
init(red: Double, green: Double, blue: Double) {
self.red = red
self.green = green
self.blue = blue
}
init(white: Double) {
red = white
green = white
blue = white
}
}
// 創(chuàng)建一個新的Color實例哈蝇,通過三種顏色的外部參數(shù)名來傳值棺妓,并調(diào)用構(gòu)造器
let magenta = Color(red: 1.0, green: 0.0, blue: 1.0)
print("red 值為: \(magenta.red)")
print("green 值為: \(magenta.green)")
print("blue 值為: \(magenta.blue)")
// 創(chuàng)建一個新的Color實例,通過三種顏色的外部參數(shù)名來傳值炮赦,并調(diào)用構(gòu)造器
let halfGray = Color(white: 0.5)
print("red 值為: \(halfGray.red)")
print("green 值為: \(halfGray.green)")
print("blue 值為: \(halfGray.blue)")
以上程序執(zhí)行輸出結(jié)果為:
red 值為: 1.0
green 值為: 0.0
blue 值為: 1.0
red 值為: 0.5
green 值為: 0.5
blue 值為: 0.5
沒有外部參數(shù)
如果你不希望為構(gòu)造器的某個參數(shù)提供外部名字怜跑,你可以使用下劃線 _ 來顯示描述它的外部名。
struct Rectangle {
var length: Double
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
//不提供外部名字
init(_ area: Double) {
length = area
}
}
// 調(diào)用不提供外部名字
let rectarea = Rectangle(180.0)
print("面積為: \(rectarea.length)")
// 調(diào)用不提供外部名字
let rearea = Rectangle(370.0)
print("面積為: \(rearea.length)")
// 調(diào)用不提供外部名字
let recarea = Rectangle(110.0)
print("面積為: \(recarea.length)")
以上程序執(zhí)行輸出結(jié)果為:
面積為: 180.0
面積為: 370.0
面積為: 110.0
可選屬性類型
如果你定制的類型包含一個邏輯上允許取值為空的儲存型屬性吠勘,你都需要將它定義為可選類型 optional type (可選屬性類型)性芬,當(dāng)存儲屬性聲明為可選時,將自動初始化為空 nil
struct Rectangle {
var length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面積為:\(rectarea.length)")
let rearea = Rectangle(370.0)
print("面積為:\(rearea.length)")
let recarea = Rectangle(110.0)
print("面積為:\(recarea.length)")
以上程序輸出的結(jié)果為:
面積為:Optional(180.0)
面積為:Optional(370.0)
面積為:Optional(110.0)
構(gòu)造過程中修改常量屬性
只要在構(gòu)造過程結(jié)束前常量的值能確定剧防,你可以在構(gòu)造過程中的任意時間點修改常量的值植锉。對于某個類實例來說,它的常量屬性只能在定義它的類的構(gòu)造過程中修改峭拘;不能在子類中修改汽煮。盡管length 屬性現(xiàn)在是常量,我們?nèi)匀豢梢栽谄漕惖臉?gòu)造器中設(shè)置它的值:
struct Rectangle {
let length: Double?
init(frombreadth breadth: Double) {
length = breadth * 10
}
init(frombre bre: Double) {
length = bre * 10
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(frombre: 180)
print(rectarea.length)
let rearea = Rectangle(frombre: 370)
print(rearea.length)
let recarea = Rectangle(110.0)
print(recarea.length)
輸出結(jié)果為:
Optional(1800.0)
Optional(3700.0)
Optional(110.0)
默認(rèn)構(gòu)造器
默認(rèn)構(gòu)造器將簡單的創(chuàng)建一個所有屬性都設(shè)置為默認(rèn)值的實例:
以下實例中棚唆,ShoppingListItem類中所有屬性都有默認(rèn)值暇赤,且它是沒有父類的基類,它將自動獲得一個可以為所有屬性設(shè)置默認(rèn)值的默認(rèn)構(gòu)造器
class ShoppingListItem {
var name: String?
var quantity = 1
var purchased = false
}
var item = ShoppingListItem()
print("名字為:\(item.name)")
print("數(shù)量為:\(item.quantity)")
print("是否已經(jīng)完成付款:\(item.purchased)")
以上程序執(zhí)行輸出結(jié)果為:
名字為:nil
數(shù)量為:1
是否已經(jīng)完成付款:false
結(jié)構(gòu)體的逐一成員構(gòu)造器
如果結(jié)構(gòu)體對所有存儲屬性提供了默認(rèn)值且自身沒有提供定制的構(gòu)造器宵凌,它們能自動逐一獲得一個逐一成員構(gòu)造器鞋囊。
我們在調(diào)用逐一成員構(gòu)造器時,通過與成員屬性名相同的參數(shù)進行傳值來完成對成員屬性的初始賦值瞎惫。
值類型的構(gòu)造器代理
構(gòu)造器可以通過調(diào)用其他構(gòu)造器來完成實例的部分構(gòu)造過程溜腐,這一過程稱為構(gòu)造代理,他能減少多個構(gòu)造器間的代碼重復(fù)瓜喇。以下實例,Rect結(jié)構(gòu)體調(diào)用了 Size 和 Point 的構(gòu)造過程:
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()
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)
}
}
// origin和size屬性都使用定義時的默認(rèn)值Point(x: 0.0, y: 0.0)和Size(width: 0.0, height: 0.0):
let basicRect = Rect()
print("Size 結(jié)構(gòu)體初始值: \(basicRect.size.width, basicRect.size.height) ")
print("Rect 結(jié)構(gòu)體初始值: \(basicRect.origin.x, basicRect.origin.y) ")
// 將origin和size的參數(shù)值賦給對應(yīng)的存儲型屬性
let originRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
print("Size 結(jié)構(gòu)體初始值: \(originRect.size.width, originRect.size.height) ")
print("Rect 結(jié)構(gòu)體初始值: \(originRect.origin.x, originRect.origin.y) ")
//先通過center和size的值計算出origin的坐標(biāo)挺益。
//然后再調(diào)用(或代理給)init(origin:size:)構(gòu)造器來將新的origin和size值賦值到對應(yīng)的屬性中
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
print("Size 結(jié)構(gòu)體初始值: \(centerRect.size.width, centerRect.size.height) ")
print("Rect 結(jié)構(gòu)體初始值: \(centerRect.origin.x, centerRect.origin.y) ")
輸出結(jié)果為:
Size 結(jié)構(gòu)體初始值: (0.0, 0.0)
Rect 結(jié)構(gòu)體初始值: (0.0, 0.0)
Size 結(jié)構(gòu)體初始值: (5.0, 5.0)
Rect 結(jié)構(gòu)體初始值: (2.0, 2.0)
Size 結(jié)構(gòu)體初始值: (3.0, 3.0)
Rect 結(jié)構(gòu)體初始值: (2.5, 2.5)
構(gòu)造器代理規(guī)則
值類型 | 類類型 |
---|---|
不支持繼承,所以狗仔妻代理的過相對簡單乘寒,因為他們只能代理給本身提供的其他構(gòu)造器望众,你可以使用 self.init在自定義的構(gòu)造器中引用其他的屬于相同值類型的構(gòu)造器。 | 它可以繼承自其他類,這意味著類有責(zé)任保證其所有繼承的儲存型屬性在構(gòu)造時也能正確的初始化烂翰。 |
類的繼承和構(gòu)造過程
Swift提供了兩種類型的構(gòu)造器來確保所有類實例中存儲屬性都能獲得初始值夯缺,他們分別是指定構(gòu)造器和遍歷構(gòu)造器。
指定構(gòu)造器 | 遍歷構(gòu)造器 |
---|---|
類中最主要的構(gòu)造器 | 類中比較次要的構(gòu)造器 |
初始化類中提供的所有屬性甘耿,并根據(jù)父類鏈上調(diào)用父類的構(gòu)造器來實現(xiàn)父類的初始化 | 可以定義便利構(gòu)造器來調(diào)用同一個類中的指定構(gòu)造器踊兜,并為其參數(shù)提供默認(rèn)值。你也可以定義便利構(gòu)造器來創(chuàng)建一個特殊用途或特定輸入的實例佳恬。 |
每個類都必須擁有至少一個置頂構(gòu)造器 | 只在必要的時候為類提供便利構(gòu)造器 |
Init(parameters){statements } |
converience init(parameters) {statements} |
指定構(gòu)造器
class mainClass {
var no1 : Int // 局部存儲變量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int // 新的子類存儲變量
init(no1 : Int, no2 : Int) {
self.no2 = no2 // 初始化
super.init(no1:no1) // 初始化超類
}
}
let res = mainClass(no1: 10)
let res2 = subClass(no1: 10, no2: 20)
print("res 為: \(res.no1)")
print("res2 為: \(res2.no1)")
print("res2 為: \(res2.no2)")
以上程序執(zhí)行輸出結(jié)果為:
res 為: 10
res 為: 10
res 為: 20
便利構(gòu)造器實例
class mainClass {
var no1 : Int // 局部存儲變量
init(no1 : Int) {
self.no1 = no1 // 初始化
}
}
class subClass : mainClass {
var no2 : Int
init(no1 : Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 便利方法只需要一個參數(shù)
override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let res2 = subClass(no1: 30, no2: 50)
print("res 為: \(res.no1)")
print("res2 為: \(res2.no1)")
print("res2 為: \(res2.no2)")
以上程序輸出結(jié)果為:
res 為: 20
res2 為: 30
res2 為: 50
構(gòu)造器的繼承和重載
Swift 中子類不會默認(rèn)繼承父類的構(gòu)造器捏境。
父類的構(gòu)造器僅在確定和安全的情況下被繼承
當(dāng)你充協(xié)議一個父類指定的構(gòu)造器時,你需要 override 關(guān)鍵字修飾
class SuperClass {
var corners = 4
var description: String {
return "\(corners) 邊"
}
}
let rectangle = SuperClass()
print("矩形: \(rectangle.description)")
class SubClass: SuperClass {
override init() { //重載構(gòu)造器
super.init()
corners = 5
}
}
let subClass = SubClass()
print("五角型: \(subClass.description)")
以上程序執(zhí)行輸出結(jié)果為:
矩形: 4 邊
五角型: 5 邊
指定構(gòu)造器和便利構(gòu)造器
它定義了包含兩個類 MainClass毁葱、SubClass的類層次結(jié)構(gòu)典蝌,并將演示他們的構(gòu)造器時如果相互作用的。
class MainClass {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[匿名]")
}
}
let main = MainClass(name: "Runoob")
print("MainClass 名字為: \(main.name)")
let main2 = MainClass()
print("沒有對應(yīng)名字: \(main2.name)")
class SubClass: MainClass {
var count: Int
init(name: String, count: Int) {
self.count = count
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, count: 1)
}
}
let sub = SubClass(name: "Runoob")
print("MainClass 名字為: \(sub.name)")
let sub2 = SubClass(name: "Runoob", count: 3)
print("count 變量: \(sub2.count)")
上述輸出:
MainClass 名字為: Runoob
沒有對應(yīng)名字: [匿名]
MainClass 名字為: Runoob
count 變量: 3
類的可失敗構(gòu)造器
如果一個類头谜,結(jié)構(gòu)體或枚舉類型的對象骏掀,在構(gòu)造自身的過程中有可能失敗,則為其定義一個可失敗構(gòu)造器柱告。
變量初始化失敗可能的原因有:
- 傳入無效的參數(shù)
- 缺少某個所需的外部資源
- 沒有滿足特定條件
為了妥善處理這種構(gòu)造過程中可能失敗的情況截驮。你可以在一個類,結(jié)構(gòu)體或枚舉中际度,添加一個或多個可失敗構(gòu)造器葵袭。其語法在 **init關(guān)鍵字后面添加問號 init?
下面例子中定義了一個名為 Animal 的結(jié)構(gòu)體,其中有一個名為 species 的 String 的常量屬性乖菱。同時結(jié)構(gòu)體還定義了一個坡锡,帶一個String類型參數(shù) specie 的可失敗構(gòu)造器。這個可失敗構(gòu)造器窒所,被用來檢查傳入的參數(shù)室友為一個空字符串鹉勒,如果是空字符串,該該失敗器構(gòu)造失敗吵取,構(gòu)造對象失敗禽额,否則成功。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
//通過該可失敗構(gòu)造器來構(gòu)建一個Animal的對象皮官,并檢查其構(gòu)建過程是否成功
// someCreature 的類型是 Animal? 而不是 Animal
let someCreature = Animal(species: "長頸鹿")
// 打印 "動物初始化為長頸鹿"
if let giraffe = someCreature {
print("動物初始化為\(giraffe.species)")
}
以上程序執(zhí)行輸出結(jié)果為:
動物初始化為長頸鹿
枚舉類型的可失敗構(gòu)造器
你可以通過構(gòu)造一個帶一個或多個參數(shù)的可失敗構(gòu)造器來獲取枚舉類型中特定的枚舉成員脯倒。
enum TemperatureUnit {
// 開爾文,攝氏捺氢,華氏
case Kelvin, Celsius, Fahrenheit
init?(symbol: Character) {
switch symbol {
case "K":
self = .Kelvin
case "C":
self = .Celsius
case "F":
self = .Fahrenheit
default:
return nil
}
}
}
let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
print("這是一個已定義的溫度單位藻丢,所以初始化成功。")
}
let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
print("這不是一個已定義的溫度單位摄乒,所以初始化失敗悠反。")
}
以上程序執(zhí)行輸出結(jié)果為:
這是一個已定義的溫度單位残黑,所以初始化成功。
這不是一個已定義的溫度單位问慎,所以初始化失敗。
類的可失敗構(gòu)造器
值類型(如結(jié)構(gòu)體或枚舉類型)的可失敗構(gòu)造器挤茄,對何時何地出發(fā)構(gòu)造失敗這個行為沒有任何的限制如叼。但是類的可失敗構(gòu)造器只能在所有的類屬性被初始化后所有類之間的代理調(diào)用發(fā)生完成后觸發(fā)失敗行為。
下面例子中穷劈,定義了一個名為StudRecord的類笼恰,因為studname 屬性是一個常量,所以一旦 StudRecord 類構(gòu)造成功歇终, studname屬性肯定有一個非nil的值社证。
class StudRecord {
let studname: String!
init?(studname: String) {
self.studname = studname
if studname.isEmpty { return nil }
}
}
if let stname = StudRecord(studname: "失敗構(gòu)造器") {
print("模塊為 \(stname.studname)")
}
以上程序執(zhí)行輸出結(jié)果為:
模塊為 失敗構(gòu)造器
覆蓋一個可失敗構(gòu)造器
就如同其他構(gòu)造器一樣,你也可以用子類的可失敗構(gòu)造器覆蓋基類的可失敗構(gòu)造器评凝∽菲希或者你也可以用子類的費可失敗構(gòu)造器覆蓋一個基類的可失敗構(gòu)造器。你可以用一個非可失敗構(gòu)造器覆蓋一個可失敗構(gòu)造器奕短,但是反過來行不通宜肉。一個非可失敗的構(gòu)造器永遠(yuǎn)也不能代理調(diào)用一個可失敗構(gòu)造器
可失敗構(gòu)造器 init!
通常來說我們通過 init 關(guān)鍵字后添加 問號 ?來定義一個可失敗構(gòu)造器,但也可以通過使用 init后添加驚嘆號的方式是來定義一個可失敗構(gòu)造器 init!翎碑。示例如下:
struct StudRecord {
let stname: String
init!(stname: String) {
if stname.isEmpty {return nil }
self.stname = stname
}
}
let stmark = StudRecord(stname: "Runoob")
if let name = stmark {
print("指定了學(xué)生名")
}
let blankname = StudRecord(stname: "")
if blankname == nil {
print("學(xué)生名為空")
}
輸出如下:
指定了學(xué)生名
學(xué)生名為空
析構(gòu)
在一個類的實例被釋放之前谬返,析構(gòu)函數(shù)被立即調(diào)用。用關(guān)鍵字 deinit 來表示析構(gòu)函數(shù)日杈,類似于初始化函數(shù) init來標(biāo)示遣铝。析構(gòu)函數(shù)只適用于類類型。與 OC 中 dealloc 一樣莉擒。
過程原理
Swift 會自動釋放不在需要的實例以釋放資源酿炸。
Swift 通過自動引用計數(shù)(ARC)處理實例的內(nèi)存管理
通常當(dāng)你的實例被釋放時不需要手動的去清理,但是涨冀,當(dāng)使用自己的資源時梁沧,你可能需要進行一些額外的清理。例如:如果創(chuàng)建了一個自定義的類打開一個文件蝇裤,并寫入一些數(shù)據(jù)廷支,你可能需要在類實例釋放之前關(guān)閉文件。
在類的定義中栓辜,每個類最多只能有一個析構(gòu)函數(shù)恋拍。析構(gòu)函數(shù)不帶任何參數(shù),在寫法上不帶括號:
deinit {
// 執(zhí)行析構(gòu)過程 釋放資源
}