目錄
繼承 構(gòu)造過(guò)程 析構(gòu)過(guò)程 可選鏈 類型轉(zhuǎn)換 協(xié)議 泛型 訪問控制 相等與相同 內(nèi)存管理
繼承
繼承我們可以理解為一個(gè)類獲取了另外一個(gè)類的方法和屬性尔当。
當(dāng)一個(gè)類繼承其它類時(shí),繼承類叫子類酪穿,被繼承類叫超類(或父類)
在 Swift 中,類可以調(diào)用和訪問超類的方法晴裹,屬性和下標(biāo)腳本被济,并且可以重寫它們。
也可以為類中繼承來(lái)的屬性添加屬性觀察器涧团。
1.基類
沒有繼承其它類的類只磷,稱之為基類(Base Class)。
以下實(shí)例中我們定義了基類 StudDetails 泌绣,描述了學(xué)生(stname)及其各科成績(jī)的分?jǐn)?shù)(mark1钮追、mark2、mark3):
class StudDetails {
var stname: String!
var mark1: Int!
var mark2: Int!
var mark3: Int!
init(stname: String, mark1: Int, mark2: Int, mark3: Int) {
self.stname = stname
self.mark1 = mark1
self.mark2 = mark2
self.mark3 = mark3
}
}
let stname = "swift"
let mark1 = 98
let mark2 = 89
let mark3 = 76
let sds = StudDetails(stname:stname, mark1:mark1, mark2:mark2, mark3:mark3);
print(sds.stname);print(sds.mark1);print(sds.mark2);print(sds.mark3)
2.子類
子類指的是在一個(gè)已有類的基礎(chǔ)上創(chuàng)建一個(gè)新的類阿迈。
為了指明某個(gè)類的超類元媚,將超類名寫在子類名的后面,用冒號(hào)(:)分隔,語(yǔ)法格式如下
class SomeClass: SomeSuperclass {
// 類的定義
}
實(shí)例
以下實(shí)例中我們定義了超類 StudDetails仿滔,然后使用子類 Tom 繼承它:
class StudDetails
{
var mark1: Int;
var mark2: Int;
init(stm1:Int, results stm2:Int)
{
mark1 = stm1;
mark2 = stm2;
}
func show()
{
print("Mark1:\(self.mark1), Mark2:\(self.mark2)")
}
}
class Tom : StudDetails
{
init()
{
super.init(stm1: 93, results: 89)
}
}
let tom = Tom()
tom.show()
以上程序執(zhí)行輸出結(jié)果為:
Mark1:93, Mark2:89
3.重寫(Overriding)
子類可以通過(guò)繼承來(lái)的實(shí)例方法惠毁,類方法,實(shí)例屬性崎页,或下標(biāo)腳本來(lái)實(shí)現(xiàn)自己的定制功能鞠绰,我們把這種行為叫重寫(overriding)。
我們可以使用 override 關(guān)鍵字來(lái)實(shí)現(xiàn)重寫飒焦。
重寫 訪問方法蜈膨,屬性屿笼,下標(biāo)腳本
方法 super.somemethod()
屬性 super.someProperty()
下標(biāo)腳本 super[someIndex]
- 重寫方法
以下實(shí)例中我們重寫了 show() 方法:
class SuperClass {
func show() {
print("這是超類 SuperClass")
}
}
class SubClass: SuperClass {
override func show() {
print("這是子類 SubClass")
}
}
let superClass = SuperClass()
superClass.show()
let subClass = SubClass()
subClass.show()
以上程序執(zhí)行輸出結(jié)果為:
這是超類 SuperClass
這是子類 SubClass
- 重寫屬性
你可以提供定制的 getter(或 setter)來(lái)重寫任意繼承來(lái)的屬性,無(wú)論繼承來(lái)的屬性是存儲(chǔ)型的還是計(jì)算型的屬性翁巍。
子類并不知道繼承來(lái)的屬性是存儲(chǔ)型的還是計(jì)算型的驴一,它只知道繼承來(lái)的屬性會(huì)有一個(gè)名字和類型。所以你在重寫一個(gè)屬性時(shí)灶壶,必需將它的名字和類型都寫出來(lái)肝断。
注意點(diǎn):
如果你在重寫屬性中提供了 setter,那么你也一定要提供 getter驰凛。
如果你不想在重寫版本中的 getter 里修改繼承來(lái)的屬性值胸懈,你可以直接通過(guò)super.someProperty來(lái)返回繼承來(lái)的值,其中someProperty是你要重寫的屬性的名字恰响。
以下實(shí)例我們定義了超類 Circle 及子類 Rectangle, 在 Rectangle 類中我們重寫屬性 area:
class Circle {
var radius = 12.5
var area: String {
return "矩形半徑 \(radius) "
}
}
// 繼承超類 Circle
class Rectangle: Circle {
var print = 7
override var area: String {
return super.area + " 趣钱,但現(xiàn)在被重寫為 \(print)"
}
}
let rect = Rectangle()
rect.radius = 25.0
rect.print = 3
print("Radius \(rect.area)")
// 重寫屬性觀察器
class Square: Rectangle {
override var radius: Double {
didSet {
print = Int(radius/5.0)+1
}
}
}
let sq = Square()
sq.radius = 100.0
print("半徑: \(sq.area)")
以上程序執(zhí)行輸出結(jié)果為:
Radius 矩形半徑 25.0 ,但現(xiàn)在被重寫為 3
半徑: 矩形半徑為 100.0 胚宦,但現(xiàn)在被重寫為 21
- 重寫屬性觀察器
你可以在屬性重寫中為一個(gè)繼承來(lái)的屬性添加屬性觀察器首有。這樣一來(lái),當(dāng)繼承來(lái)的屬性值發(fā)生改變時(shí)枢劝,你就會(huì)監(jiān)測(cè)到井联。
注意:你不可以為繼承來(lái)的常量存儲(chǔ)型屬性或繼承來(lái)的只讀計(jì)算型屬性添加屬性觀察器。
4.防止重寫
我們可以使用 final 關(guān)鍵字防止它們被重寫呈野。
如果你重寫了final方法低矮,屬性或下標(biāo)腳本印叁,在編譯時(shí)會(huì)報(bào)錯(cuò)被冒。
你可以通過(guò)在關(guān)鍵字class前添加final特性(final class)來(lái)將整個(gè)類標(biāo)記為 final 的,這樣的類是不可被繼承的轮蜕,否則會(huì)報(bào)編譯錯(cuò)誤昨悼。
final class Circle {
final var radius = 12.5
var area: String {
return "矩形半徑為 \(radius) "
}
}
構(gòu)造過(guò)程
構(gòu)造過(guò)程是為了使用某個(gè)類、結(jié)構(gòu)體或枚舉類型的實(shí)例而進(jìn)行的準(zhǔn)備過(guò)程跃洛。這個(gè)過(guò)程包含了為實(shí)例中的每個(gè)屬性設(shè)置初始值和為其執(zhí)行必要的準(zhǔn)備和初始化任務(wù)率触。
Swift 構(gòu)造函數(shù)使用 init() 方法。
與 Objective-C 中的構(gòu)造器不同汇竭,Swift 的構(gòu)造器無(wú)需返回值葱蝗,它們的主要任務(wù)是保證新實(shí)例在第一次使用前完成正確的初始化。
類實(shí)例也可以通過(guò)定義析構(gòu)器(deinitializer)在類實(shí)例釋放之前執(zhí)行清理內(nèi)存的工作细燎。
1.存儲(chǔ)型屬性的初始賦值
類和結(jié)構(gòu)體在實(shí)例創(chuàng)建時(shí)两曼,必須為所有存儲(chǔ)型屬性設(shè)置合適的初始值。
存儲(chǔ)屬性在構(gòu)造器中賦值時(shí)玻驻,它們的值是被直接設(shè)置的悼凑,不會(huì)觸發(fā)任何屬性觀測(cè)器。
存儲(chǔ)屬性在構(gòu)造器中賦值流程:
- 創(chuàng)建初始值。
- 在屬性定義中指定默認(rèn)屬性值户辫。
- 初始化實(shí)例渐夸,并調(diào)用 init() 方法。
2.構(gòu)造器
構(gòu)造器在創(chuàng)建某特定類型的新實(shí)例時(shí)調(diào)用渔欢。它的最簡(jiǎn)形式類似于一個(gè)不帶任何參數(shù)的實(shí)例方法墓塌,以關(guān)鍵字init命名。
- 語(yǔ)法
init()
{
// 實(shí)例化后執(zhí)行的代碼
}
實(shí)例
以下結(jié)構(gòu)體定義了一個(gè)不帶參數(shù)的構(gòu)造器 init奥额,并在里面將存儲(chǔ)型屬性 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)")
以上程序執(zhí)行輸出結(jié)果為:
矩形面積為 72.0
- 默認(rèn)屬性值
我們可以在構(gòu)造器中為存儲(chǔ)型屬性設(shè)置初始值桃纯;同樣,也可以在屬性聲明時(shí)為其設(shè)置默認(rèn)值披坏。
使用默認(rèn)值能讓你的構(gòu)造器更簡(jiǎn)潔态坦、更清晰,且能通過(guò)默認(rèn)值自動(dòng)推導(dǎo)出屬性的類型棒拂。
以下實(shí)例我們?cè)趯傩月暶鲿r(shí)為其設(shè)置默認(rèn)值:
struct rectangle {
// 設(shè)置默認(rèn)值
var length = 6
var breadth = 12
}
var area = rectangle()
print("矩形的面積為 \(area.length*area.breadth)")
- 構(gòu)造參數(shù)
你可以在定義構(gòu)造器 init() 時(shí)提供構(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)")
以上程序執(zhí)行輸出結(jié)果為:
面積為: 72.0
面積為: 432.0
內(nèi)部和外部參數(shù)名
跟函數(shù)和方法參數(shù)相同,構(gòu)造參數(shù)也存在一個(gè)在構(gòu)造器內(nèi)部使用的參數(shù)名字和一個(gè)在調(diào)用構(gòu)造器時(shí)使用的外部參數(shù)名字帚屉。
然而谜诫,構(gòu)造器并不像函數(shù)和方法那樣在括號(hào)前有一個(gè)可辨別的名字。所以在調(diào)用構(gòu)造器時(shí)攻旦,主要通過(guò)構(gòu)造器中的參數(shù)名和類型來(lái)確定需要調(diào)用的構(gòu)造器喻旷。
如果你在定義構(gòu)造器時(shí)沒有提供參數(shù)的外部名字,Swift 會(huì)為每個(gè)構(gòu)造器的參數(shù)自動(dòng)生成一個(gè)跟內(nèi)部名字相同的外部名牢屋。沒有外部名稱參數(shù)
如果你不希望為構(gòu)造器的某個(gè)參數(shù)提供外部名字且预,你可以使用下劃線_來(lái)顯示描述它的外部名。
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)")
以上程序執(zhí)行輸出結(jié)果為:
面積為: 180.0
- 可選屬性類型
如果你定制的類型包含一個(gè)邏輯上允許取值為空的存儲(chǔ)型屬性烙无,你都需要將它定義為可選類型optional type(可選屬性類型)锋谐。
當(dāng)存儲(chǔ)屬性聲明為可選時(shí),將自動(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)")
以上程序執(zhí)行輸出結(jié)果為:
面積為:Optional(180.0)
- 構(gòu)造過(guò)程中修改常量屬性
只要在構(gòu)造過(guò)程結(jié)束前常量的值能確定涮拗,你可以在構(gòu)造過(guò)程中的任意時(shí)間點(diǎn)修改常量屬性的值。
對(duì)某個(gè)類實(shí)例來(lái)說(shuō)迂苛,它的常量屬性只能在定義它的類的構(gòu)造過(guò)程中修改三热;不能在子類中修改。
盡管 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 * 30
}
init(_ area: Double) {
length = area
}
}
let rectarea = Rectangle(180.0)
print("面積為:\(rectarea.length)")
以上程序執(zhí)行輸出結(jié)果為:
面積為:Optional(180.0)
默認(rèn)構(gòu)造器
默認(rèn)構(gòu)造器將簡(jiǎn)單的創(chuàng)建一個(gè)所有屬性值都設(shè)置為默認(rèn)值的實(shí)例;
類中的所有屬性都有默認(rèn)值就漾,且它是沒有父類的基類,它將自動(dòng)獲得一個(gè)可以為所有屬性設(shè)置默認(rèn)值的默認(rèn)構(gòu)造器值類型的構(gòu)造器代理
構(gòu)造器可以通過(guò)調(diào)用其它構(gòu)造器來(lái)完成實(shí)例的部分構(gòu)造過(guò)程赌髓。這一過(guò)程稱為構(gòu)造器代理从藤,它能減少多個(gè)構(gòu)造器間的代碼重復(fù)催跪。
以下實(shí)例中,Rect 結(jié)構(gòu)體調(diào)用了 Size 和 Point 的構(gòu)造過(guò)程:
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屬性都使用定義時(shí)的默認(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ù)值賦給對(duì)應(yīng)的存儲(chǔ)型屬性
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) ")
//先通過(guò)center和size的值計(jì)算出origin的坐標(biāo)夷野。
//然后再調(diào)用(或代理給)init(origin:size:)構(gòu)造器來(lái)將新的origin和size值賦值到對(duì)應(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) ")
以上程序執(zhí)行輸出結(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)造器代理的過(guò)程相對(duì)簡(jiǎn)單,因?yàn)樗鼈冎荒艽斫o本身提供的其它構(gòu)造器悯搔。 你可以使用self.init在自定義的構(gòu)造器中引用其它的屬于相同值類型的構(gòu)造器骑丸。 - 類類型
它可以繼承自其它類,這意味著類有責(zé)任保證其所有繼承的存儲(chǔ)型屬性在構(gòu)造時(shí)也能正確的初始化。
- 值類型
-
類的繼承和構(gòu)造過(guò)程
Swift 提供了兩種類型的類構(gòu)造器來(lái)確保所有類實(shí)例中存儲(chǔ)型屬性都能獲得初始值妒貌,它們分別是指定構(gòu)造器和便利構(gòu)造器通危。- 指定構(gòu)造器
類中最主要的構(gòu)造器
初始化類中提供的所有屬性,并根據(jù)父類鏈往上調(diào)用父類的構(gòu)造器來(lái)實(shí)現(xiàn)父類的初始化灌曙。
每一個(gè)類都必須擁有至少一個(gè)指定構(gòu)造器
Init(parameters) {
statements
} - 便利構(gòu)造器
類中比較次要的菊碟、輔助型的構(gòu)造器
可以定義便利構(gòu)造器來(lái)調(diào)用同一個(gè)類中的指定構(gòu)造器,并為其參數(shù)提供默認(rèn)值在刺。你也可以定義便利構(gòu)造器來(lái)創(chuàng)建一個(gè)特殊用途或特定輸入的實(shí)例逆害。
只在必要的時(shí)候?yàn)轭愄峁┍憷麡?gòu)造器
convenience init(parameters) {
statements
}
- 指定構(gòu)造器
構(gòu)造器的繼承和重載
Swift 中的子類不會(huì)默認(rèn)繼承父類的構(gòu)造器。
父類的構(gòu)造器僅在確定和安全的情況下被繼承蚣驼。
當(dāng)你重寫一個(gè)父類指定構(gòu)造器時(shí)魄幕,你需要寫override修飾符。
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è)類颖杏,結(jié)構(gòu)體或枚舉類型的對(duì)象纯陨,在構(gòu)造自身的過(guò)程中有可能失敗,則為其定義一個(gè)可失敗構(gòu)造器留储。
變量初始化失敗可能的原因有:
* 傳入無(wú)效的參數(shù)值翼抠。
* 缺少某種所需的外部資源。
* 沒有滿足特定條件欲鹏。
為了妥善處理這種構(gòu)造過(guò)程中可能會(huì)失敗的情況机久。
你可以在一個(gè)類臭墨,結(jié)構(gòu)體或是枚舉類型的定義中赔嚎,添加一個(gè)或多個(gè)可失敗構(gòu)造器。其語(yǔ)法為在init關(guān)鍵字后面加添問號(hào)(init?)胧弛。
下例中尤误,定義了一個(gè)名為Animal的結(jié)構(gòu)體,其中有一個(gè)名為species的结缚,String類型的常量屬性损晤。
同時(shí)該結(jié)構(gòu)體還定義了一個(gè),帶一個(gè)String類型參數(shù)species的,可失敗構(gòu)造器尤勋。這個(gè)可失敗構(gòu)造器暖哨,被用來(lái)檢查傳入的參數(shù)是否為一個(gè)空字符串沛慢,如果為空字符串团甲,則該可失敗構(gòu)造器圾另,構(gòu)建對(duì)象失敗坡椒,否則成功汗唱。
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
//通過(guò)該可失敗構(gòu)造器來(lái)構(gòu)建一個(gè)Animal的對(duì)象,并檢查其構(gòu)建過(guò)程是否成功
// someCreature 的類型是 Animal? 而不是 Animal
let someCreature = Animal(species: "長(zhǎng)頸鹿")
// 打印 "動(dòng)物初始化為長(zhǎng)頸鹿"
if let giraffe = someCreature {
print("動(dòng)物初始化為\(giraffe.species)")
}
以上程序執(zhí)行輸出結(jié)果為:
動(dòng)物初始化為長(zhǎng)頸鹿
類的可失敗構(gòu)造器
值類型(如結(jié)構(gòu)體或枚舉類型)的可失敗構(gòu)造器,對(duì)何時(shí)何地觸發(fā)構(gòu)造失敗這個(gè)行為沒有任何的限制榜旦。
但是猿挚,類的可失敗構(gòu)造器只能在所有的類屬性被初始化后和所有類之間的構(gòu)造器之間的代理調(diào)用發(fā)生完后觸發(fā)失敗行為。
覆蓋一個(gè)可失敗構(gòu)造器
就如同其它構(gòu)造器一樣,你也可以用子類的可失敗構(gòu)造器覆蓋基類的可失敗構(gòu)造器佳鳖。
者你也可以用子類的非可失敗構(gòu)造器覆蓋一個(gè)基類的可失敗構(gòu)造器霍殴。
你可以用一個(gè)非可失敗構(gòu)造器覆蓋一個(gè)可失敗構(gòu)造器,但反過(guò)來(lái)卻行不通系吩。
一個(gè)非可失敗的構(gòu)造器永遠(yuǎn)也不能代理調(diào)用一個(gè)可失敗構(gòu)造器来庭。
可失敗構(gòu)造器 init!
通常來(lái)說(shuō)我們通過(guò)在init關(guān)鍵字后添加問號(hào)的方式(init?)來(lái)定義一個(gè)可失敗構(gòu)造器,但你也可以使用通過(guò)在init后面添加驚嘆號(hào)的方式來(lái)定義一個(gè)可失敗構(gòu)造器(init!)穿挨。
析構(gòu)過(guò)程
在一個(gè)類的實(shí)例被釋放之前月弛,析構(gòu)函數(shù)被立即調(diào)用。用關(guān)鍵字deinit來(lái)標(biāo)示析構(gòu)函數(shù)絮蒿,類似于初始化函數(shù)用init來(lái)標(biāo)示尊搬。析構(gòu)函數(shù)只適用于類類型。
- 析構(gòu)過(guò)程原理
Swift 會(huì)自動(dòng)釋放不再需要的實(shí)例以釋放資源土涝。
Swift 通過(guò)自動(dòng)引用計(jì)數(shù)(ARC)處理實(shí)例的內(nèi)存管理。
通常當(dāng)你的實(shí)例被釋放時(shí)不需要手動(dòng)地去清理幌墓。但是但壮,當(dāng)使用自己的資源時(shí)冀泻,你可能需要進(jìn)行一些額外的清理。
例如蜡饵,如果創(chuàng)建了一個(gè)自定義的類來(lái)打開一個(gè)文件弹渔,并寫入一些數(shù)據(jù),你可能需要在類實(shí)例被釋放之前關(guān)閉該文件溯祸。 - 語(yǔ)法
在類的定義中肢专,每個(gè)類最多只能有一個(gè)析構(gòu)函數(shù)。析構(gòu)函數(shù)不帶任何參數(shù)焦辅,在寫法上不帶括號(hào):
deinit {
// 執(zhí)行析構(gòu)過(guò)程
}
實(shí)例
var counter = 0; // 引用計(jì)數(shù)器
class BaseClass {
init() {
counter += 1;
}
deinit {
counter -= 1;
}
}
var show: BaseClass? = BaseClass()
print(counter)
show = nil
print(counter)
以上程序執(zhí)行輸出結(jié)果為:1 0
可選鏈
可選鏈(Optional Chaining)是一種可以請(qǐng)求和調(diào)用屬性博杖、方法和子腳本的過(guò)程,用于請(qǐng)求或調(diào)用的目標(biāo)可能為nil筷登。
可選鏈返回兩個(gè)值:
如果目標(biāo)有值剃根,調(diào)用就會(huì)成功,返回該值
如果目標(biāo)為nil前方,調(diào)用將返回nil
多次請(qǐng)求或調(diào)用可以被鏈接成一個(gè)鏈狈醉,如果任意一個(gè)節(jié)點(diǎn)為nil將導(dǎo)致整條鏈?zhǔn)А?/p>
- 可選鏈可替代強(qiáng)制解析
通過(guò)在屬性、方法惠险、或下標(biāo)腳本的可選值后面放一個(gè)問號(hào)(?)苗傅,即可定義一個(gè)可選鏈。- 可選鏈 '?'
? 放置于可選值后來(lái)調(diào)用方法班巩,屬性金吗,下標(biāo)腳本
當(dāng)可選為 nil 輸出比較友好的錯(cuò)誤信息 當(dāng) - 感嘆號(hào)(!)強(qiáng)制展開方法,屬性趣竣,下標(biāo)腳本可選鏈
! 放置于可選值后來(lái)調(diào)用方法摇庙,屬性,下標(biāo)腳本來(lái)強(qiáng)制展開值
可選為 nil 時(shí)強(qiáng)制展開執(zhí)行錯(cuò)誤
- 可選鏈 '?'
使用感嘆號(hào)(!)可選鏈實(shí)例
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
//將導(dǎo)致運(yùn)行時(shí)錯(cuò)誤
let roomCount = john.residence!.numberOfRooms
以上程序執(zhí)行輸出結(jié)果為:
fatal error: unexpectedly found nil while unwrapping an Optional value
想使用感嘆號(hào)(!)強(qiáng)制解析獲得這個(gè)人residence屬性numberOfRooms屬性值遥缕,將會(huì)引發(fā)運(yùn)行時(shí)錯(cuò)誤卫袒,因?yàn)檫@時(shí)沒有可以供解析的residence值。
使用問號(hào)(?)可選鏈實(shí)例
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
// 鏈接可選residence?屬性单匣,如果residence存在則取回numberOfRooms的值
if let roomCount = john.residence?.numberOfRooms {
print("John 的房間號(hào)為 \(roomCount)夕凝。")
} else {
print("不能查看房間號(hào)")
}
以上程序執(zhí)行輸出結(jié)果為:
不能查看房間號(hào)
因?yàn)檫@種嘗試獲得numberOfRooms的操作有可能失敗,可選鏈會(huì)返回Int?類型值户秤,或者稱作"可選Int"码秉。當(dāng)residence是空的時(shí)候(上例),選擇Int將會(huì)為空鸡号,因此會(huì)出現(xiàn)無(wú)法訪問numberOfRooms的情況转砖。
要注意的是,即使numberOfRooms是非可選Int(Int?)時(shí)這一點(diǎn)也成立。只要是通過(guò)可選鏈的請(qǐng)求就意味著最后numberOfRooms總是返回一個(gè)Int?而不是Int府蔗。
為可選鏈定義模型類
你可以使用可選鏈來(lái)多層調(diào)用屬性晋控,方法,和下標(biāo)腳本姓赤。這讓你可以利用它們之間的復(fù)雜模型來(lái)獲取更底層的屬性赡译,并檢查是否可以成功獲取此類底層屬性。通過(guò)可選鏈調(diào)用方法
你可以使用可選鏈的來(lái)調(diào)用可選值的方法并檢查方法調(diào)用是否成功不铆。即使這個(gè)方法沒有返回值蝌焚,你依然可以使用可選鏈來(lái)達(dá)成這一目的。
if ((john.residence?.printNumberOfRooms()) != nil)
- 使用可選鏈調(diào)用下標(biāo)腳本
你可以使用可選鏈來(lái)嘗試從下標(biāo)腳本獲取值并檢查下標(biāo)腳本的調(diào)用是否成功誓斥,然而只洒,你不能通過(guò)可選鏈來(lái)設(shè)置下標(biāo)腳本。
if let firstRoomName = john.residence?[0].name
- 通過(guò)可選鏈接調(diào)用來(lái)訪問下標(biāo)
通過(guò)可選鏈接調(diào)用岖食,我們可以用下標(biāo)來(lái)對(duì)可選值進(jìn)行讀取或?qū)懭牒毂⑶遗袛嘞聵?biāo)調(diào)用是否成功。
if let firstRoomName = john.residence?[0].name
- 連接多層鏈接
你可以將多層可選鏈連接在一起泡垃,可以掘取模型內(nèi)更下層的屬性方法和下標(biāo)腳本析珊。然而多層可選鏈不能再添加比已經(jīng)返回的可選值更多的層。
如果你試圖通過(guò)可選鏈獲得Int值蔑穴,不論使用了多少層鏈接返回的總是Int?忠寻。 相似的,如果你試圖通過(guò)可選鏈獲得Int?值存和,不論使用了多少層鏈接返回的總是Int?奕剃。
if let johnsStreet = john.residence?.address?.street
- 對(duì)返回可選值的函數(shù)進(jìn)行鏈接
我們還可以通過(guò)可選鏈接來(lái)調(diào)用返回可空值的方法,并且可以繼續(xù)對(duì)可選值進(jìn)行鏈接捐腿。
類型轉(zhuǎn)換
類型轉(zhuǎn)換
Swift 語(yǔ)言類型轉(zhuǎn)換可以判斷實(shí)例的類型纵朋。也可以用于檢測(cè)實(shí)例類型是否屬于其父類或者子類的實(shí)例。
Swift 中類型轉(zhuǎn)換使用 is 和 as 操作符實(shí)現(xiàn)茄袖,is 用于檢測(cè)值的類型操软,as 用于轉(zhuǎn)換類型。
類型轉(zhuǎn)換也可以用來(lái)檢查一個(gè)類是否實(shí)現(xiàn)了某個(gè)協(xié)議宪祥。
檢查類型
類型轉(zhuǎn)換用于檢測(cè)實(shí)例類型是否屬于特定的實(shí)例類型聂薪。
你可以將它用在類和子類的層次結(jié)構(gòu)上,檢查特定類實(shí)例的類型并且轉(zhuǎn)換這個(gè)類實(shí)例的類型成為這個(gè)層次結(jié)構(gòu)中的其他類型蝗羊。
類型檢查使用 is 關(guān)鍵字藏澳。
操作符 is 來(lái)檢查一個(gè)實(shí)例是否屬于特定子類型。若實(shí)例屬于那個(gè)子類型耀找,類型檢查操作符返回 true翔悠,否則返回 false。向下轉(zhuǎn)型
向下轉(zhuǎn)型,用類型轉(zhuǎn)換操作符(as? 或 as!)
當(dāng)你不確定向下轉(zhuǎn)型可以成功時(shí)凉驻,用類型轉(zhuǎn)換的條件形式(as?)腻要。條件形式的類型轉(zhuǎn)換總是返回一個(gè)可選值(optional value)复罐,并且若下轉(zhuǎn)是不可能的涝登,可選值將是 nil。
只有你可以確定向下轉(zhuǎn)型一定會(huì)成功時(shí)效诅,才使用強(qiáng)制形式(as!)胀滚。當(dāng)你試圖向下轉(zhuǎn)型為一個(gè)不正確的類型時(shí),強(qiáng)制形式的類型轉(zhuǎn)換會(huì)觸發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤乱投。Any和AnyObject的類型轉(zhuǎn)換
Swift為不確定類型提供了兩種特殊類型別名:
AnyObject可以代表任何class類型的實(shí)例咽笼。
Any可以表示任何類型,包括方法類型(function types)戚炫。
注意:
只有當(dāng)你明確的需要它的行為和功能時(shí)才使用Any和AnyObject剑刑。在你的代碼里使用你期望的明確的類型總是更好的。
協(xié)議
指定了類和結(jié)構(gòu)需要實(shí)現(xiàn)的方法和變量双肤,optional關(guān)鍵字可以定義協(xié)議中選擇實(shí)現(xiàn)的方法和變量施掏,一個(gè)結(jié)構(gòu)體或者類可以實(shí)現(xiàn)多個(gè)協(xié)議,值得注意的是協(xié)議之間是可以繼承的茅糜。
- 語(yǔ)法
協(xié)議的語(yǔ)法格式如下:
protocol SomeProtocol {
// 協(xié)議內(nèi)容
}
要使類遵循某個(gè)協(xié)議七芭,需要在類型名稱后加上協(xié)議名稱,中間以冒號(hào):分隔蔑赘,作為類型定義的一部分狸驳。遵循多個(gè)協(xié)議時(shí),各協(xié)議之間用逗號(hào),分隔缩赛。
struct SomeStructure: FirstProtocol, AnotherProtocol {
// 結(jié)構(gòu)體內(nèi)容
}
如果類在遵循協(xié)議的同時(shí)擁有父類耙箍,應(yīng)該將父類名放在協(xié)議名之前,以逗號(hào)分隔酥馍。
class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
// 類的內(nèi)容
}
對(duì)屬性的規(guī)定
協(xié)議用于指定特定的實(shí)例屬性或類屬性辩昆,而不用指定是存儲(chǔ)型屬性或計(jì)算型屬性。此外還必須指明是只讀的還是可讀可寫的物喷。
協(xié)議中的通常用var來(lái)聲明變量屬性卤材,在類型聲明后加上{ set get }來(lái)表示屬性是可讀可寫的,只讀屬性則用{ get }來(lái)表示峦失。對(duì) Mutating 方法的規(guī)定
有時(shí)需要在方法中改變它的實(shí)例扇丛。
例如,值類型(結(jié)構(gòu)體尉辑,枚舉)的實(shí)例方法中帆精,將mutating關(guān)鍵字作為函數(shù)的前綴,寫在func之前,表示可以在該方法中修改它所屬的實(shí)例及其實(shí)例屬性的值卓练。對(duì)構(gòu)造器的規(guī)定
協(xié)議可以要求它的遵循者實(shí)現(xiàn)指定的構(gòu)造器隘蝎。
你可以像書寫普通的構(gòu)造器那樣,在協(xié)議的定義里寫下構(gòu)造器的聲明襟企,但不需要寫花括號(hào)和構(gòu)造器的實(shí)體嘱么,語(yǔ)法如下:
protocol SomeProtocol {
init(someParameter: Int)
}
實(shí)例
protocol tcpprotocol {
init(aprot: Int)
}
- 在擴(kuò)展中添加協(xié)議成員
我們可以可以通過(guò)擴(kuò)展來(lái)擴(kuò)充已存在類型( 類,結(jié)構(gòu)體顽悼,枚舉等)曼振。
擴(kuò)展可以為已存在的類型添加屬性,方法蔚龙,下標(biāo)腳本冰评,協(xié)議等成員。
protocol AgeClasificationProtocol {
var age: Int { get }
func agetype() -> String
}
class Person {
let firstname: String
let lastname: String
var age: Int
init(firstname: String, lastname: String) {
self.firstname = firstname
self.lastname = lastname
self.age = 10
}
}
extension Person : AgeClasificationProtocol {
func fullname() -> String {
var c: String
c = firstname + " " + lastname
return c
}
func agetype() -> String {
switch age {
case 0...2:
return "Baby"
case 2...12:
return "Child"
case 13...19:
return "Teenager"
case let x where x > 65:
return "Elderly"
default:
return "Normal"
}
}
}
- 協(xié)議的繼承
協(xié)議能夠繼承一個(gè)或多個(gè)其他協(xié)議木羹,可以在繼承的協(xié)議基礎(chǔ)上增加新的內(nèi)容要求甲雅。
協(xié)議的繼承語(yǔ)法與類的繼承相似,多個(gè)被繼承的協(xié)議間用逗號(hào)分隔:
protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
// 協(xié)議定義
}
- 類專屬協(xié)議
你可以在協(xié)議的繼承列表中,通過(guò)添加class關(guān)鍵字,限制協(xié)議只能適配到類(class)類型坑填。
該class關(guān)鍵字必須是第一個(gè)出現(xiàn)在協(xié)議的繼承列表中抛人,其后,才是其他繼承協(xié)議穷遂。格式如下:
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// 協(xié)議定義
}
實(shí)例
protocol TcpProtocol {
init(no1: Int)
}
class MainClass {
var no1: Int // 局部變量
init(no1: Int) {
self.no1 = no1 // 初始化
}
}
class SubClass: MainClass, TcpProtocol {
var no2: Int
init(no1: Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// 因?yàn)樽裱瓍f(xié)議函匕,需要加上"required"; 因?yàn)槔^承自父類,需要加上"override"
required override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = MainClass(no1: 20)
let show = SubClass(no1: 30, no2: 50)
print("res is: \(res.no1)")
print("res is: \(show.no1)")
print("res is: \(show.no2)")
- 協(xié)議合成
Swift 支持合成多個(gè)協(xié)議蚪黑,這在我們需要同時(shí)遵循多個(gè)協(xié)議時(shí)非常有用盅惜。
語(yǔ)法格式如下:
protocol Stname {
var name: String { get }
}
protocol Stage {
var age: Int { get }
}
struct Person: Stname, Stage {
var name: String
var age: Int
}
func show(celebrator: Stname & Stage) {
print("\(celebrator.name) is \(celebrator.age) years old")
}
let studname = Person(name: "Priya", age: 21)
print(studname)
let stud = Person(name: "Rehan", age: 29)
print(stud)
let student = Person(name: "Roshan", age: 19)
print(student)
以上程序執(zhí)行輸出結(jié)果為:
Person(name: "Priya", age: 21)
Person(name: "Rehan", age: 29)
Person(name: "Roshan", age: 19)
- 檢驗(yàn)協(xié)議的一致性
你可以使用is和as操作符來(lái)檢查是否遵循某一協(xié)議或強(qiáng)制轉(zhuǎn)化為某一類型。
is操作符用來(lái)檢查實(shí)例是否遵循了某個(gè)協(xié)議忌穿。
as?返回一個(gè)可選值抒寂,當(dāng)實(shí)例遵循協(xié)議時(shí),返回該協(xié)議類型;否則返回nil掠剑。
as用以強(qiáng)制向下轉(zhuǎn)型屈芜,如果強(qiáng)轉(zhuǎn)失敗,會(huì)引起運(yùn)行時(shí)錯(cuò)誤朴译。
實(shí)例
protocol HasArea {
var area: Double { get }
}
for object in objects {
// 對(duì)迭代出的每一個(gè)元素進(jìn)行檢查井佑,看它是否遵循了HasArea協(xié)議
if let objectWithArea = object as? HasArea {
print("面積為 \(objectWithArea.area)")
} else {
print("沒有面積")
}
}
- 多協(xié)議
protocol AreaComputetionProtocol {
func computeArea() -> Double
}
protocol PerimeterComputetionProtocol {
func computePerimeter() -> Double
}
struct RectAngle: AreaComputetionProtocol, PerimeterComputetionProtocol {
var width, height: Double
internal func computeArea() -> Double {
return width*height
}
internal func computePerimeter() -> Double {
return 2*(width + height)
}
}
let rect: RectAngle = RectAngle(width: 3.0, height: 4.0)
print(rect.computeArea())
print(rect.computePerimeter())
//協(xié)議繼承
protocol TriangeleProtocol: AreaComputetionProtocol, PerimeterComputetionProtocol {
var a: Double {get set}
var b: Double {get set}
var c: Double {get set}
var base: Double {get set}
var height: Double {get set}
}
struct Triangle: TriangeleProtocol {
var a: Double = 0.0
var b: Double = 0.0
var c: Double = 0.0
var base: Double = 0.0
var height: Double = 0.0
func computeArea() -> Double {
return base*height/2.0
}
func computePerimeter() -> Double {
return a + b + c
}
}
let triangle: Triangle = Triangle(a: 3, b: 4, c: 5, base: 3, height: 4)
print(triangle.computeArea())
print(triangle.computePerimeter())
說(shuō)到協(xié)議就不得不說(shuō)委托,委托是為了讓一個(gè)類或結(jié)構(gòu)體能夠?qū)⒐ぷ骱蜎Q策交給另一個(gè)類或結(jié)構(gòu)去完成眠寿。
//委托:讓一個(gè)類或結(jié)構(gòu)能夠?qū)⒐ぷ骱蜎Q策交給另一個(gè)類或結(jié)構(gòu)去完成
/// 售貨機(jī)協(xié)議
protocol VendingMathineProtocol {
/// 是否投幣
var coinInserted: Bool {get set}
/// 能否售貨
func shouldVend() -> Bool
}
/// 自動(dòng)售貨機(jī)類躬翁,遵守售貨機(jī)協(xié)議
class Vendor: VendingMathineProtocol {
var coinInserted: Bool = false
/// 如果投幣則售貨否則不售貨
///
/// - Returns: 是否售貨
func shouldVend() -> Bool {
if coinInserted {
coinInserted = false
return true
}
else
{
return false
}
}
}
/// 可樂機(jī)類
class ColaMethine {
// 自動(dòng)售貨機(jī)類,遵守售貨機(jī)協(xié)議
var vendor: VendingMathineProtocol
init(vendor: VendingMathineProtocol) {
self.vendor = vendor
}
/// 投幣
func insertCoin() {
vendor.coinInserted = true
}
/// 銷售可樂按鈕事件
func pressColaButton() -> String {
if vendor.shouldVend() {
return "Here's a cola!"
}
else
{
return "You must insert coin!"
}
}
/// 銷售啤酒按鈕事件
func pressRootBeerButton() -> String {
if vendor.shouldVend() {
return "Here's a Root Beer!"
}
else
{
return "You must insert coin!"
}
}
}
let methine: ColaMethine = ColaMethine.init(vendor: Vendor.init())
print(methine.pressColaButton())
methine.insertCoin()
print(methine.pressColaButton())
methine.insertCoin()
print(methine.pressRootBeerButton())
print(methine.pressColaButton())
泛型
Swift 提供了泛型讓你寫出靈活且可重用的函數(shù)和類型盯拱。
Swift 標(biāo)準(zhǔn)庫(kù)是通過(guò)泛型代碼構(gòu)建出來(lái)的盒发。
Swift 的數(shù)組和字典類型都是泛型集例嘱。
不指定數(shù)據(jù)的類型,只是指定一個(gè)占位符宁舰。編譯器負(fù)責(zé)對(duì)數(shù)據(jù)類型進(jìn)行檢查拼卵。使用方式如下:
func valueEqual<T: Equatable>(value1: T, value2: T) -> Bool {
return value1 == value2
}
代碼來(lái)看,它們功能代碼是相同的蛮艰,只是類型上不一樣腋腮,這時(shí)我們可以使用泛型,從而避免重復(fù)編寫代碼印荔。
泛型使用了占位類型名(在這里用字母 T 來(lái)表示)來(lái)代替實(shí)際類型名(例如 Int低葫、String 或 Double)详羡。
func swapTwoValues<T>(_ a: inout T, _ b: inout T)
swapTwoValues 后面跟著占位類型名(T)仍律,并用尖括號(hào)括起來(lái)(<T>)。這個(gè)尖括號(hào)告訴 Swift 那個(gè) T 是 swapTwoValues(::) 函數(shù)定義內(nèi)的一個(gè)占位類型名实柠,因此 Swift 不會(huì)去查找名為 T 的實(shí)際類型水泉。
以下實(shí)例是一個(gè)泛型函數(shù) exchange 用來(lái)交換兩個(gè) Int 和 String 值:
// 定義一個(gè)交換兩個(gè)變量的函數(shù)
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var numb1 = 100
var numb2 = 200
print("交換前數(shù)據(jù): \(numb1) 和 \(numb2)")
swapTwoValues(&numb1, &numb2)
print("交換后數(shù)據(jù): \(numb1) 和 \(numb2)")
var str1 = "A"
var str2 = "B"
print("交換前數(shù)據(jù): \(str1) 和 \(str2)")
swapTwoValues(&str1, &str2)
print("交換后數(shù)據(jù): \(str1) 和 \(str2)")
以上程序執(zhí)行輸出結(jié)果為:
交換前數(shù)據(jù): 100 和 200
交換后數(shù)據(jù): 200 和 100
交換前數(shù)據(jù): A 和 B
交換后數(shù)據(jù): B 和 A
泛型類型
Swift 允許你定義你自己的泛型類型。
自定義類窒盐、結(jié)構(gòu)體和枚舉作用于任何類型草则,如同 Array 和 Dictionary 的用法。
接下來(lái)我們來(lái)編寫一個(gè)名為 Stack (棧)的泛型集合類型蟹漓,棧只允許在集合的末端添加新的元素(稱之為入棧)炕横,且也只能從末端移除元素(稱之為出棧)。類型約束
類型約束指定了一個(gè)必須繼承自指定類的類型參數(shù)葡粒,或者遵循一個(gè)特定的協(xié)議或協(xié)議構(gòu)成份殿。
類型約束語(yǔ)法
你可以寫一個(gè)在一個(gè)類型參數(shù)名后面的類型約束,通過(guò)冒號(hào)分割嗽交,來(lái)作為類型參數(shù)鏈的一部分卿嘲。這種作用于泛型函數(shù)的類型約束的基礎(chǔ)語(yǔ)法如下所示(和泛型類型的語(yǔ)法相同):
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// 這里是泛型函數(shù)的函數(shù)體部分
}
上面這個(gè)函數(shù)有兩個(gè)類型參數(shù)。第一個(gè)類型參數(shù) T夫壁,有一個(gè)要求 T 必須是 SomeClass 子類的類型約束拾枣;第二個(gè)類型參數(shù) U,有一個(gè)要求 U 必須符合 SomeProtocol 協(xié)議的類型約束盒让。
實(shí)例
// 非泛型函數(shù)梅肤,查找指定字符串在數(shù)組中的索引
func findIndex(ofString valueToFind: String, in array: [String]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
// 找到返回索引值
return index
}
}
return nil
}
let strings = ["google", "weibo", "taobao", "runoob", "facebook"]
if let foundIndex = findIndex(ofString: "runoob", in: strings) {
print("runoob 的索引為 \(foundIndex)")
}
索引下標(biāo)從 0 開始。
以上程序執(zhí)行輸出結(jié)果為:
runoob 的索引為 3
- 關(guān)聯(lián)類
Swift 中使用 associatedtype 關(guān)鍵字來(lái)設(shè)置關(guān)聯(lián)類型實(shí)例邑茄。
下面例子定義了一個(gè) Container 協(xié)議姨蝴,該協(xié)議定義了一個(gè)關(guān)聯(lián)類型 ItemType。
Container 協(xié)議只指定了三個(gè)任何遵從 Container 協(xié)議的類型必須提供的功能撩扒。遵從協(xié)議的類型在滿足這三個(gè)條件的情況下也可以提供其他額外的功能似扔。
// Container 協(xié)議
protocol Container {
associatedtype ItemType
// 添加一個(gè)新元素到容器里
mutating func append(_ item: ItemType)
// 獲取容器中元素的數(shù)
var count: Int { get }
// 通過(guò)索引值類型為 Int 的下標(biāo)檢索到容器中的每一個(gè)元素
subscript(i: Int) -> ItemType { get }
}
// Stack 結(jié)構(gòu)體遵從 Container 協(xié)議
struct Stack<Element>: Container {
// Stack<Element> 的原始實(shí)現(xiàn)部分
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container 協(xié)議的實(shí)現(xiàn)部分
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
// 元素列表
print(tos.items)
// 元素個(gè)數(shù)
print( tos.count)
以上程序執(zhí)行輸出結(jié)果為:
["google", "runoob", "taobao"]
3
- Where 語(yǔ)句
類型約束能夠確保類型符合泛型函數(shù)或類的定義約束吨些。
你可以在參數(shù)列表中通過(guò)where語(yǔ)句定義參數(shù)的約束。
你可以寫一個(gè)where語(yǔ)句炒辉,緊跟在在類型參數(shù)列表后面豪墅,where語(yǔ)句后跟一個(gè)或者多個(gè)針對(duì)關(guān)聯(lián)類型的約束,以及(或)一個(gè)或多個(gè)類型和關(guān)聯(lián)類型間的等價(jià)(equality)關(guān)系黔寇。
實(shí)例
下面的例子定義了一個(gè)名為allItemsMatch的泛型函數(shù)偶器,用來(lái)檢查兩個(gè)Container實(shí)例是否包含相同順序的相同元素。
如果所有的元素能夠匹配缝裤,那么返回 true屏轰,反之則返回 false。
// Container 協(xié)議
protocol Container {
associatedtype ItemType
// 添加一個(gè)新元素到容器里
mutating func append(_ item: ItemType)
// 獲取容器中元素的數(shù)
var count: Int { get }
// 通過(guò)索引值類型為 Int 的下標(biāo)檢索到容器中的每一個(gè)元素
subscript(i: Int) -> ItemType { get }
}
// // 遵循Container協(xié)議的泛型TOS類型
struct Stack<Element>: Container {
// Stack<Element> 的原始實(shí)現(xiàn)部分
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
// Container 協(xié)議的實(shí)現(xiàn)部分
mutating func append(_ item: Element) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Element {
return items[i]
}
}
// 擴(kuò)展憋飞,將 Array 當(dāng)作 Container 來(lái)使用
extension Array: Container {}
func allItemsMatch<C1: Container, C2: Container>
(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.ItemType == C2.ItemType, C1.ItemType: Equatable {
// 檢查兩個(gè)容器含有相同數(shù)量的元素
if someContainer.count != anotherContainer.count {
return false
}
// 檢查每一對(duì)元素是否相等
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
// 所有元素都匹配霎苗,返回 true
return true
}
var tos = Stack<String>()
tos.push("google")
tos.push("runoob")
tos.push("taobao")
var aos = ["google", "runoob", "taobao"]
if allItemsMatch(tos, aos) {
print("匹配所有元素")
} else {
print("元素不匹配")
}
以上程序執(zhí)行輸出結(jié)果為:
匹配所有元素
- 運(yùn)算符重載:只需定義一個(gè)運(yùn)算符命名的函數(shù)就可以對(duì)運(yùn)算符進(jìn)行重載。
訪問控制
- public 可以訪問自己模塊中源文件里的任何實(shí)體榛做,別人也可以通過(guò)引入該模塊來(lái)訪問源文件里的所有實(shí)體唁盏。
- internal 可以訪問自己模塊中源文件里的任何實(shí)體,但是別人不能訪問該模塊中源文件里的實(shí)體检眯。
- fileprivate 文件內(nèi)私有厘擂,只能在當(dāng)前源文件中使用。
- private 只能在類中訪問锰瘸,離開了這個(gè)類或者結(jié)構(gòu)體的作用域外面就無(wú)法訪問刽严。
1.public 為最高級(jí)訪問級(jí)別,private 為最低級(jí)訪問級(jí)別避凝。
2.元組的訪問級(jí)別與元組中訪問級(jí)別最低的類型一致
3.枚舉中成員的訪問級(jí)別繼承自該枚舉舞萄,你不能為枚舉中的成員單獨(dú)申明不同的訪問級(jí)別。
4.函數(shù)的訪問級(jí)別需要根據(jù)該函數(shù)的參數(shù)類型和返回類型的訪問級(jí)別得出恕曲。
5.子類的訪問級(jí)別不得高于父類的訪問級(jí)別鹏氧。比如說(shuō),父類的訪問級(jí)別是internal佩谣,子類的訪問級(jí)別就不能申明為public把还。
6.常量、變量茸俭、屬性不能擁有比它們的類型更高的訪問級(jí)別吊履。
比如說(shuō),你定義一個(gè)public級(jí)別的屬性调鬓,但是它的類型是private級(jí)別的艇炎,這是編譯器所不允許的。
同樣腾窝,下標(biāo)也不能擁有比索引類型或返回類型更高的訪問級(jí)別缀踪。
如果常量居砖、變量、屬性驴娃、下標(biāo)索引的定義類型是private級(jí)別的奏候,那么它們必須要明確的申明訪問級(jí)別為private:
7.如果想為一個(gè)協(xié)議明確的申明訪問級(jí)別,那么需要注意一點(diǎn)唇敞,就是你要確保該協(xié)議只在你申明的訪問級(jí)別作用域中使用蔗草。
如果你定義了一個(gè)public訪問級(jí)別的協(xié)議,那么實(shí)現(xiàn)該協(xié)議提供的必要函數(shù)也會(huì)是public的訪問級(jí)別疆柔。這一點(diǎn)不同于其他類型咒精,比如,public訪問級(jí)別的其他類型旷档,他們成員的訪問級(jí)別為internal模叙。
8.任何你定義的類型別名都會(huì)被當(dāng)作不同的類型,以便于進(jìn)行訪問控制彬犯。一個(gè)類型別名的訪問級(jí)別不可高于原類型的訪問級(jí)別向楼。
9.常量、變量谐区、屬性、下標(biāo)索引的Getters和Setters的訪問級(jí)別繼承自它們所屬成員的訪問級(jí)別逻卖。
擴(kuò)展
不繼承而擴(kuò)展類的功能宋列,擴(kuò)展能夠以非侵入式的方式,增加類评也、結(jié)構(gòu)炼杖、甚至基本類型的行為和功能,類似于Objective_C的類別盗迟,但是強(qiáng)大得多坤邪。基本使用如下:
擴(kuò)展就是向一個(gè)已有的類罚缕、結(jié)構(gòu)體或枚舉類型添加新功能艇纺。
擴(kuò)展可以對(duì)一個(gè)類型添加新的功能,但是不能重寫已有的功能邮弹。
// MARK: - 可樂機(jī)的擴(kuò)展
extension ColaMethine {
/// 健怡可樂按鈕事件
func pressDietColaButton() -> String {
if vendor.shouldVend() {
return "Here's a Diet Cola!"
}
else
{
return "You must insert coin!"
}
}
}
methine.insertCoin()
print(methine.pressDietColaButton())
1.擴(kuò)展基本數(shù)據(jù)類型:不能在擴(kuò)展中添加常規(guī)存儲(chǔ)屬性黔衡,但可以添加計(jì)算屬性:值是通過(guò)計(jì)算獲得的屬性
let limit: Double = 1024.0
// MARK: - 為Int64擴(kuò)展屬性,獲取存儲(chǔ)單位的換算結(jié)果
extension Int64 {
var K: String {return String.init(format: "%fK", Double(self)/limit)}
var M: String {return String.init(format: "%fM", Double(self)/limit/limit)}
var G: String {return String.init(format: "%fG", Double(self)/limit/limit/limit)}
var T: String {return String.init(format: "%fT", Double(self)/limit/limit/limit/limit)}
}
let bytes: Int64 = 2345346457
print(bytes.K)
print(bytes.M)
print(bytes.G)
print(bytes.T)
2.mutating關(guān)鍵字:如果要修改自身的值而不是返回計(jì)算的結(jié)果腌乡,那么就得使用mutating關(guān)鍵字
// MARK: - 為Double數(shù)據(jù)類型擴(kuò)展方法
extension Double {
/// 平方值
mutating func squra() {
self = self*self
}
/// 立方值
mutating func cube() {
self = self*self*self
}
}
var num: Double = 1.5
num.squra()
print(num)
num.cube()
print(num)
相等與相同
相等是指值相等盟劫,相同是指指向的對(duì)象為同一個(gè)對(duì)象。使用==來(lái)判斷值的相等与纽,用===來(lái)判斷是否為同一個(gè)對(duì)象侣签。
let a: Int = 1
let b: Int = 1
class IntClass {
var value: Int = 0
init(value: Int) {
self.value = value
}
}
let intA: IntClass = IntClass.init(value: 1)
let intB: IntClass = IntClass.init(value: 1)
print(a == b)
print(a != b)
print(intA === intB)
print(intA !== intB)
Swift內(nèi)存管理
Swift以ARC方式為引用類型數(shù)據(jù)管理內(nèi)存塘装,這種方式是由編譯器提供支持的。每一個(gè)引用類型的數(shù)據(jù)都有一個(gè)引用計(jì)數(shù)的的屬性影所,當(dāng)這個(gè)對(duì)象被創(chuàng)建的時(shí)候它的引用計(jì)數(shù)就為1氢哮,當(dāng)這個(gè)對(duì)象在應(yīng)用程序運(yùn)行過(guò)程中被傳遞,就可能被多個(gè)“所有者”持有并使用型檀。當(dāng)“所有者”獲得所有權(quán)時(shí)引用計(jì)算就相應(yīng)加1冗尤,而在放棄所有權(quán)時(shí)引用計(jì)數(shù)就相應(yīng)減1,直到引用計(jì)數(shù)為0時(shí)(所有的“所有者”都放棄了所有權(quán))胀溺,對(duì)象就會(huì)被銷毀裂七,它所占用的內(nèi)存歸還給內(nèi)存池,供其他對(duì)象使用仓坞。
1.循環(huán)引用:循環(huán)引用就是指不同對(duì)象之間相互持有對(duì)方背零,這樣造成了對(duì)象永遠(yuǎn)被“所有者”所占有,而無(wú)法釋放无埃,從而形成內(nèi)存泄漏徙瓶。
- 普通對(duì)象之間的循環(huán)引用:這種循環(huán)引用可以使用weak關(guān)鍵字來(lái)打破。
- 閉包內(nèi)的循環(huán)引用:閉包是引用類型的數(shù)據(jù)嫉称,類的實(shí)例變量強(qiáng)引用了閉包侦镇,而如果閉包中再引用了類對(duì)象的話,就會(huì)造成循環(huán)引用织阅。在閉包定義中添加"[unowned self] in"可以解決閉包內(nèi)的循環(huán)引用壳繁。
Swift 提供了兩種辦法用來(lái)解決你在使用類的屬性時(shí)所遇到的循環(huán)強(qiáng)引用問題:
弱引用(weak)
無(wú)主引用(unowned)
弱引用和無(wú)主引用允許循環(huán)引用中的一個(gè)實(shí)例引用另外一個(gè)實(shí)例而不保持強(qiáng)引用。這樣實(shí)例能夠互相引用而不產(chǎn)生循環(huán)強(qiáng)引用荔棉。
對(duì)于生命周期中會(huì)變?yōu)閚il的實(shí)例使用弱引用闹炉。相反的,對(duì)于初始化賦值后再也不會(huì)被賦值為nil的實(shí)例润樱,使用無(wú)主引用渣触。
說(shuō)明:一個(gè)對(duì)象在銷毀的時(shí)候會(huì)調(diào)用deinit,而一個(gè)變量賦值為nil時(shí)就會(huì)放棄對(duì)原有對(duì)象的持有壹若。