Swift 程式語言 — Extension

讓我們看看 Swift 的 Extension 能做到哪些事情吧!

Jeremy Xue
Jeremy Xue ‘s Blog
8 min readOct 27, 2019

--

Photo by Nastuh Abootalebi on Unsplash

前言:

擴展(Extension)為現有的 class、struct、enum 或 protocol 類型添加了新功能。這包括擴展你無法訪問的原始碼的類型的能力(稱為 retroactive modeling)。擴展與 Objective-C 中的 categories 相似。(不同於 Objective-C 的 categories,Swift 的擴展沒有名稱。)

Swift 中的擴展能夠:

  • 添加計算實例屬性和計算類型屬性
  • 定義實例方法和類型方法
  • 提供新初始化器
  • 定義下標
  • 定義和使用內嵌類型
  • 使現有類型遵循協議

在 Swift 中,你甚至可以擴展協議提供其要求的實現,或添加符合類型可以使用的額外功能。詳情可以參考 Protocol Extensions

|擴展語法

使用 extension 關鍵字宣告擴展:

擴展可以擴展現有類型來使其採用一個或多個協議。添加協議一致性,請使用和 classstruct 編寫協議名稱相同的方式編寫協議名稱:

在以 Adding Protocol Conformance with an Extension 中描述了以這種方式添加協議一制性。

擴展可以用於擴展現有的通用類型,如 Extending a Generic Type 中所述。你還可以擴展泛型類型以有條件的添加功能,如 Extensions with a Generic Where Clause 所述。

|計算屬性

擴展可以添加計算實例屬性和計算類型屬性到現有類型。這個範例添加 5 個計算實例屬性到 Swift 內建的 Double 類型中,為距離單位提供基本的支持:

這些計算屬性表達 Double 值應該視為某個長度單位。儘管他們實現為計算屬性,但是可以使用點語法將這些屬性的名稱附加到浮點數文字值上,以作為使用該文字值執行距離轉換的一種方式。

在這個範例中,Double1.0 被視為代表為 “一公尺”。這就是為什麼 m 計算屬性返回 self 的原因,其表達式 1.m 被視為可以計算 Double1.0

其他單位需要某些轉換來表示以公尺作為標準的值。1 公里等於 1000 公尺,因此 km 計算得出的屬性將值乘以 1_000.00 來轉換為以公尺表示的數字。同樣的,1 公尺中有 3.28084 英尺,因此 ft 計算屬性將基礎 Double 值除以 3.28084 來將其從英尺轉換為公尺。

這些屬性是唯讀的計算屬性,因此為了簡潔,它們不使用 get 關鍵字表示。它們的返回值是 Double 類型,並且可以在接受 Double 的地方進行數學運算:

|初始化器

擴展可以為現有的類型添加新的初始化器。這使你能夠擴展其他類型來接受自定義的類型作為初始化器參數,或者提供沒有包含在該類型原始實現中的額外初始化選項。

擴展可以將新的便捷初始化器添加到 class,但是不能添加新的指定初始化器或反初始化器到 class。指定初始化器和反初始化器必須總是由原始的 class 實現中提供。

如果你使用擴展來為值類型新增一個初始化器,其為所有存儲屬性提供默認值並且沒有定義任何自定義初始化器,你可以從擴展的初始化器中調用該類型的默認初始化器和成員初始化器。如果你將初始化器編寫為值類型原始實現的一部分,則情況就並非如此,如同 Initializer Delegation for Value Types 中所述。

如果你使用擴展將初始化器添加到另一個模塊中宣告的 struct 中,則新的初始化器只有再從定義模塊調用初始化器後才能訪問 self

下面的範例定義了自定義的 Rect struct 來表示一個幾何矩形。該範例還定義了兩個支援的 struct,稱為 SizePoint,兩個結構為其所有屬性提供默認值 0.0

因為 Rect 為其所有屬性提供默認值,所以它會自動接收默認初始化器和成員初始化器,如 Default Initializers 中所述。這些初始化器可用於創建新的 Rect 實例:

你可以擴展 Rect 來提供採用特定的 centersize 的額外初始化器:

這個新的初始化器透過提供的中心點和尺寸值計算合適的原點。然後初始化器會調用 struct 的自動成員初始化器 init(origin:size:),該初始化器將新的 originsize 值存儲在適當的屬性中:

|方法

擴展可以向現有實例添加新的實例方法和類型方法。下面的範例向 Int 類型添加一個新的實例方法,稱為 repetitions

repetitions(task:) 方法採用 () -> Void 的單個參數,該參數表示沒有參數且不返回參數的函數。

在定義這個擴展後,你可以在任何整數上調用 repetitions(task:) 方法來執行多次任務:

執行結果:

|變異實例方法

擴展添加的實例方法還可以修改(或變異)實例本身。修改自身或其屬性的 structenum 方法必須將實例方法標記為 mutating,就像從原始實現中的變異方法一樣。

下面的範例在 SwiftInt 類型中添加了一個名為 square 的變異方法,該方法將原始值進行平方:

|下標

擴展可以新增下標到現有類型。這個範例向 Swift 內建的 Int 類型添加了一個整數下標。這個下標[n]返回數字右邊開始的第 n 位的數字:

  • 123456789[0] returns 9
  • 123456789[1] returns 8

…以此類推:

如果 Int 值的位數不足以用於請求的索引,則下標實現將返回 0,就好像該數字已在左側填充了 0:

|內嵌類型

擴展可以新增內嵌類型到現有的 classstructenum 中:

這個範例對 Int 類型添加一個內嵌的 enum,稱為 Kind,標示特定整數的數字種類。具體來說,其表示數字是正數、負數或是零。

範例中還向 Int 添加了一個名為 kind 的計算實例屬性,表示特定整數的數字種類,該屬性返回該整數的相應 kind 枚舉用例。

現在可以將內嵌 enum 與任何一組 Int 值一起使用:

印出結果:

函數 printIntegerKinds(_:) 接受一個 Int 值的陣列,並且遍歷這些值。對於陣列中的每個整數,函數將考慮該整數的 kind 計算屬性,並且印出適當的描述。

--

--

Jeremy Xue
Jeremy Xue ‘s Blog

Hi, I’m Jeremy. [好想工作室 — iOS Developer]