1. 程式人生 > >[學習筆記]菜鳥教程Swift易忘知識點總結(六)

[學習筆記]菜鳥教程Swift易忘知識點總結(六)

自動引用計數(ARC)

Swift 使用自動引用計數(ARC)這一機制來跟蹤和管理應用程式的記憶體。
通常情況下我們不需要去手動釋放記憶體,因為 ARC 會在類的例項不再被使用時,自動釋放其佔用的記憶體。

ARC 功能

  • 當每次使用 init() 方法建立一個類的新的例項的時候,ARC 會分配一大塊記憶體用來儲存例項的資訊。
  • 記憶體中會包含例項的型別資訊,以及這個例項所有相關屬性的值。
  • 當例項不再被使用時,ARC 釋放例項所佔用的記憶體,並讓釋放的記憶體能挪作他用。
  • 為了確保使用中的例項不會被銷燬,ARC 會跟蹤和計算每一個例項正在被多少屬性,常量和變數所引用。
  • 例項賦值給屬性、常量或變數,它們都會建立此例項的強引用,只要強引用還在,例項是不允許被銷燬的。

類例項之間的迴圈強引用

兩個類例項互相保持對方的強引用,並讓對方不被銷燬。這就是所謂的迴圈強引用。

解決例項之間的迴圈強引用:
Swift 提供了兩種辦法用來解決你在使用類的屬性時所遇到的迴圈強引用問題:

  • 弱引用 weak:弱引用和無主引用允許迴圈引用中的一個例項引用另外一個例項而不保持強引用。這樣例項能夠互相引用而不產生迴圈強引用。
  • 無主引用 unowned:對於生命週期中會變為nil的例項使用弱引用。相反的,對於初始化賦值後再也不會被賦值為nil的例項,使用無主引用。

閉包引起的迴圈強引用

迴圈強引用還會發生在當你將一個閉包賦值給類例項的某個屬性,並且這個閉包體中又使用了例項。這個閉包體中可能訪問了例項的某個屬性,例如self.someProperty,或者閉包中呼叫了例項的某個方法,例如self.someMethod。這兩種情況都導致了閉包 “捕獲” self,從而產生了迴圈強引用。

弱引用和無主引用

  • 當閉包和捕獲的例項總是互相引用時並且總是同時銷燬時,將閉包內的捕獲定義為無主引用。
  • 相反的,當捕獲引用有時可能會是nil時,將閉包內的捕獲定義為弱引用。
  • 如果捕獲的引用絕對不會置為nil,應該用無主引用,而不是弱引用。

型別轉換

  • Swift 語言型別轉換可以判斷例項的型別。也可以用於檢測例項型別是否屬於其父類或者子類的例項。
  • Swift 中型別轉換使用 is 和 as 操作符實現,is 用於檢測值的型別,as 用於轉換型別。
  • 型別轉換也可以用來檢查一個類是否實現了某個協議。

檢查型別

  • 型別轉換用於檢測例項型別是否屬於特定的例項型別。
  • 可以將它用在類和子類的層次結構上,檢查特定類例項的型別並且轉換這個類例項的型別成為這個層次結構中的其他型別。
  • 型別檢查使用 is 關鍵字。
  • 操作符 is 來檢查一個例項是否屬於特定子型別。若例項屬於那個子型別,型別檢查操作符返回 true,否則返回 false。

向下轉型

  • 向下轉型,用型別轉換操作符(as? 或 as!)
  • 當你不確定向下轉型可以成功時,用型別轉換的條件形式(as?)。條件形式的型別轉換總是返回一個可選值(optional value),並且若下轉是不可能的,可選值將是 nil。
  • 只有你可以確定向下轉型一定會成功時,才使用強制形式(as!)。當你試圖向下轉型為一個不正確的型別時,強制形式的型別轉換會觸發一個執行時錯誤。

Any和AnyObject的型別轉換

Swift為不確定型別提供了兩種特殊類型別名:

  • AnyObject可以代表任何class型別的例項。
  • Any可以表示任何型別,包括方法型別(function types)。

注意:只有當你明確的需要它的行為和功能時才使用Any和AnyObject。在你的程式碼裡使用你期望的明確的型別總是更好的。

擴充套件

擴充套件就是向一個已有的類、結構體或列舉型別新增新功能。
擴充套件可以對一個型別新增新的功能,但是不能重寫已有的功能。

Swift 中的擴充套件可以:

  • 新增計算型屬性和計算型靜態屬性
  • 定義例項方法和型別方法
  • 提供新的構造器
  • 定義下標
  • 定義和使用新的巢狀型別
  • 使一個已有型別符合某個協議

語法
擴充套件宣告使用關鍵字 extension:

extension SomeType {
    // 加到SomeType的新功能寫到這裡
}

一個擴充套件可以擴充套件一個已有型別,使其能夠適配一個或多個協議,語法格式如下:

extension SomeType: SomeProtocol, AnotherProctocol {
    // 協議實現寫到這裡
}

計算型屬性

擴充套件可以向已有型別新增計算型例項屬性和計算型型別屬性。

構造器

  • 擴充套件可以向已有型別新增新的構造器。
  • 這可以讓你擴充套件其它型別,將你自己的定製型別作為構造器引數,或者提供該型別的原始實現中沒有包含的額外初始化選項。
  • 擴充套件可以向類中新增新的便利構造器 init(),但是它們不能向類中新增新的指定構造器或解構函式 deinit() 。

方法

擴充套件可以向已有型別新增新的例項方法和型別方法。

可變例項方法

  • 通過擴充套件新增的例項方法也可以修改該例項本身。
  • 結構體和列舉型別中修改self或其屬性的方法必須將該例項方法標註為mutating,正如來自原始實現的修改方法一樣。

下標

擴充套件可以向一個已有型別新增新下標。

巢狀型別

擴充套件可以向已有的類、結構體和列舉新增新的巢狀型別

協議

  • 規定了用來實現某一特定功能所必需的方法和屬性。
  • 任意能夠滿足協議要求的型別被稱為遵循(conform)這個協議。
  • 類,結構體或列舉型別都可以遵循協議,並提供具體實現來完成協議定義的方法和功能。

語法

協議的語法格式如下:
protocol SomeProtocol {
    // 協議內容
}

要使類遵循某個協議,需要在型別名稱後加上協議名稱,中間以冒號:分隔,作為型別定義的一部分。遵循多個協議時,各協議之間用逗號,分隔。

struct SomeStructure: FirstProtocol, AnotherProtocol {
    // 結構體內容
}

如果類在遵循協議的同時擁有父類,應該將父類名放在協議名之前,以逗號分隔。

class SomeClass: SomeSuperClass, FirstProtocol, AnotherProtocol {
    // 類的內容
}

對屬性的規定

  • 協議用於指定特定的例項屬性或類屬性,而不用指定是儲存型屬性或計算型屬性。此外還必須指明是隻讀的還是可讀可寫的。
  • 協議中的通常用var來宣告變數屬性,在型別聲明後加上{ set get }來表示屬性是可讀可寫的,只讀屬性則用{ get }來表示。

對 Mutating 方法的規定

  • 有時需要在方法中改變它的例項。
  • 例如,值型別(結構體,列舉)的例項方法中,將mutating關鍵字作為函式的字首,寫在func之前,表示可以在該方法中修改它所屬的例項及其例項屬性的值。

對構造器的規定

協議可以要求它的遵循者實現指定的構造器。
你可以像書寫普通的構造器那樣,在協議的定義裡寫下構造器的宣告,但不需要寫花括號和構造器的實體,語法如下:

protocol SomeProtocol {
   init(someParameter: Int)
}

協議構造器規定在類中的實現

  • 你可以在遵循該協議的類中實現構造器,並指定其為類的指定構造器或者便利構造器。在這兩種情況下,都必須給構造器實現標上"required"修飾符。

  • 使用required修飾符可以保證:所有的遵循該協議的子類,同樣能為構造器規定提供一個顯式的實現或繼承實現。

協議型別

儘管協議本身並不實現任何功能,但是協議可以被當做型別來使用。
協議可以像其他普通型別一樣使用,使用場景:

  • 作為函式、方法或構造器中的引數型別或返回值型別
  • 作為常量、變數或屬性的型別
  • 作為陣列、字典或其他容器中的元素型別

在擴充套件中新增協議成員

  • 我們可以可以通過擴充套件來擴充已存在型別( 類,結構體,列舉等)。
  • 擴充套件可以為已存在的型別新增屬性,方法,下標指令碼,協議等成員。

協議的繼承

協議能夠繼承一個或多個其他協議,可以在繼承的協議基礎上增加新的內容要求。
協議的繼承語法與類的繼承相似,多個被繼承的協議間用逗號分隔:

protocol InheritingProtocol: SomeProtocol, AnotherProtocol {
    // 協議定義
}

類專屬協議

你可以在協議的繼承列表中,通過新增class關鍵字,限制協議只能適配到類(class)型別。
該class關鍵字必須是第一個出現在協議的繼承列表中,其後,才是其他繼承協議。格式如下:

protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
    // 協議定義
}

協議合成

Swift 支援合成多個協議,這在我們需要同時遵循多個協議時非常有用。

檢驗協議的一致性

可以使用is和as操作符來檢查是否遵循某一協議或強制轉化為某一型別。

  • is操作符用來檢查例項是否遵循了某個協議。
  • as?返回一個可選值,當例項遵循協議時,返回該協議型別;否則返回nil。
  • as用以強制向下轉型,如果強轉失敗,會引起執行時錯誤。

泛型

  • Swift 提供了泛型讓你寫出靈活且可重用的函式和型別。
  • Swift 標準庫是通過泛型程式碼構建出來的。
  • Swift 的陣列和字典型別都是泛型集。

泛型型別

  • Swift 允許你定義你自己的泛型型別。
  • 自定義類、結構體和列舉作用於任何型別,如同 Array 和 Dictionary 的用法。

擴充套件泛型型別

當你擴充套件一個泛型型別的時候(使用 extension 關鍵字),你並不需要在擴充套件的定義中提供型別引數列表。更加方便的是,原始型別定義中宣告的型別引數列表在擴充套件裡是可以使用的,並且這些來自原始型別中的引數名稱會被用作原始定義中型別引數的引用。

型別約束

型別約束指定了一個必須繼承自指定類的型別引數,或者遵循一個特定的協議或協議構成。

型別約束語法

可以寫一個在一個型別引數名後面的型別約束,通過冒號分割,來作為型別引數鏈的一部分。這種作用於泛型函式的型別約束的基礎語法如下所示(和泛型型別的語法相同):

func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
    // 這裡是泛型函式的函式體部分
}

關聯類

  • Swift 中使用 associatedtype 關鍵字來設定關聯型別例項。

Where 語句

  • 型別約束能夠確保型別符合泛型函式或類的定義約束。
  • 可以在引數列表中通過where語句定義引數的約束。
  • 可以寫一個where語句,緊跟在在型別引數列表後面,where語句後跟一個或者多個針對關聯型別的約束,以及(或)一個或多個型別和關聯型別間的等價(equality)關係。

訪問控制

  • 訪問控制可以限定其他原始檔或模組中程式碼對你程式碼的訪問級別。
  • 可以明確地給單個型別(類、結構體、列舉)設定訪問級別,也可以給這些型別的屬性、函式、初始化方法、基本型別、下標索引等設定訪問級別。
  • 協議也可以被限定在一定的範圍內使用,包括協議裡的全域性常量、變數和函式。
  • 訪問控制基於模組與原始檔。
  • 模組指的是以獨立單元構建和釋出的 Framework 或 Application。在 Swift 中的一個模組可以使用 import 關鍵字引入另外一個模組。
  • 原始檔是單個原始碼檔案,它通常屬於一個模組, 原始檔可以包含多個類和函式 的定義。
  • Swift 為程式碼中的實體提供了四種不同的訪問級別:public、internal、fileprivate、private。
訪問級別 定義
public 可以訪問自己模組中原始檔裡的任何實體,別人也可以通過引入該模組來訪問原始檔裡的所有實體。
internal 可以訪問自己模組中原始檔裡的任何實體,但是別人不能訪問該模組中原始檔裡的實體。
fileprivate 檔案內私有,只能在當前原始檔中使用。
private 只能在類中訪問,離開了這個類或者結構體的作用域外面就無法訪問。

函式型別訪問許可權

函式的訪問級別需要根據該函式的引數型別和返回型別的訪問級別得出。

列舉型別訪問許可權

列舉中成員的訪問級別繼承自該列舉,你不能為列舉中的成員單獨申明不同的訪問級別。

子類訪問許可權

子類的訪問級別不得高於父類的訪問級別。比如說,父類的訪問級別是internal,子類的訪問級別就不能申明為public。

常量、變數、屬性、下標訪問許可權

  • 常量、變數、屬性不能擁有比它們的型別更高的訪問級別。
  • 下標也不能擁有比索引型別或返回型別更高的訪問級別。

Getter 和 Setter訪問許可權

  • 常量、變數、屬性、下標索引的Getters和Setters的訪問級別繼承自它們所屬成員的訪問級別。
  • Setter的訪問級別可以低於對應的Getter的訪問級別,這樣就可以控制變數、屬性或下標索引的讀寫許可權。

構造器和預設構造器訪問許可權

  • 初始化
    我們可以給自定義的初始化方法申明訪問級別,但是要不高於它所屬類的訪問級別。但必要構造器例外,它的訪問級別必須和所屬類的訪問級別相同。
    如同函式或方法引數,初始化方法引數的訪問級別也不能低於初始化方法的訪問級別。
  • 預設初始化方法
    Swift為結構體、類都提供了一個預設的無參初始化方法,用於給它們的所有屬性提供賦值操作,但不會給出具體值。
    預設初始化方法的訪問級別與所屬型別的訪問級別相同。

協議訪問許可權

  • 如果想為一個協議明確的申明訪問級別,那麼需要注意一點,就是要確保該協議在你申明的訪問級別作用域中使用。
  • 如果定義了一個public訪問級別的協議,那麼實現該協議提供的必要函式也會是public的訪問級別。這一點不同於其他型別,比如,public訪問級別的其他型別,他們成員的訪問級別為internal。

擴充套件訪問許可權

  • 你可以在條件允許的情況下對類、結構體、列舉進行擴充套件。擴充套件成員應該具有和原始類成員一致的訪問級別。比如擴充套件了一個公共型別,那麼新加的成員應該具有和原始成員一樣的預設的internal訪問級別。
  • 或者,可以明確申明擴充套件的訪問級別(比如使用private extension)給該擴充套件內所有成員申明一個新的預設訪問級別。這個新的預設訪問級別仍然可以被單獨成員所申明的訪問級別所覆蓋。

泛型訪問許可權

泛型型別或泛型函式的訪問級別取泛型型別、函式本身、泛型型別引數三者中的最低訪問級別。

類型別名

  • 任何你定義的類型別名都會被當作不同的型別,以便於進行訪問控制。一個類型別名的訪問級別不可高於原型別的訪問級別。
  • 比如說,一個private級別的類型別名可以設定給一個public、internal、private的型別,但是一個public級別的類型別名只能設定給一個public級別的型別,不能設定給internal或private 級別的型別。

注意:這條規則也適用於為滿足協議一致性而給相關型別命名別名的情況。