Swift語(yǔ)法2

目錄

繼承   構(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)造器的繼承和重載
    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ì)象的持有壹若。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末嗅钻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舌稀,更是在濱河造成了極大的恐慌啊犬,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件壁查,死亡現(xiàn)場(chǎng)離奇詭異觉至,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)睡腿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門语御,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)峻贮,“玉大人,你說(shuō)我怎么就攤上這事应闯∠丝兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵碉纺,是天一觀的道長(zhǎng)船万。 經(jīng)常有香客問我,道長(zhǎng)骨田,這世上最難降的妖魔是什么耿导? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮态贤,結(jié)果婚禮上舱呻,老公的妹妹穿的比我還像新娘。我一直安慰自己悠汽,他們只是感情好箱吕,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著柿冲,像睡著了一般茬高。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姻采,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天雅采,我揣著相機(jī)與錄音,去河邊找鬼慨亲。 笑死,一個(gè)胖子當(dāng)著我的面吹牛宝鼓,可吹牛的內(nèi)容都是我干的刑棵。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼愚铡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蛉签!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起沥寥,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤碍舍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后邑雅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體片橡,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年淮野,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了捧书。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吹泡。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖经瓷,靈堂內(nèi)的尸體忽然破棺而出爆哑,到底是詐尸還是另有隱情,我是刑警寧澤舆吮,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布揭朝,位于F島的核電站,受9級(jí)特大地震影響色冀,放射性物質(zhì)發(fā)生泄漏潭袱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一呐伞、第九天 我趴在偏房一處隱蔽的房頂上張望敌卓。 院中可真熱鬧,春花似錦伶氢、人聲如沸趟径。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜗巧。三九已至,卻和暖如春蕾盯,著一層夾襖步出監(jiān)牢的瞬間幕屹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工级遭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留望拖,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓挫鸽,卻偏偏與公主長(zhǎng)得像说敏,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丢郊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容