Swift. Singleton

讓我們來看看什麼是單例模式吧!

Jeremy Xue
Jeremy Xue ‘s Blog

--

Photo by Colton Jones on Unsplash

▸ 前言:

單例模式經常出現在 iOS 中,他們通常被稱為 Share Instance,你幾乎常常看見他們:FileMangerUserDefaultURLSessionUIApplication 等…。而這些內容在你的應用中本質上是唯一的,因此我們可以透過單例模式來實現這種單一性,並且以簡單的方式訪問此共享對象。

而透過單例成為共享資源的對象,同時也會類似於一個全域變數,他可能導致你在測試上更為困難,也無法分離,加上同時如果有多個線程訪問時,可能會導致異常,因此使用上必須多加考量。

▸ 實現 Singleton

首先,我們可以參考維基百科中對於 Singleton 的定義:

實現單例模式的思路是:一個類能返回對象一個引用(永遠是同一個)和一個獲得該實例的方法(必須是靜態方法,通常使用getInstance這個名稱);當我們調用這個方法時,如果類持有的引用不為空就返回這個引用,如果類保持的引用為空就創建該類的實例並將實例的引用賦予該類保持的引用;同時我們還將該類的構造函數定義為私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。

因此,我們可以參考下面 Objective-C 的方式, 編寫出一個 Singleton:

這邊我們編寫靜態方法 getShareInstance 用來獲取我們的 Singleton 物件,並且其中會判斷是否存在,有的話直接返回 singletion,沒有的話就產生一個 Singleton 物件。(有點類似於 lazy 的效果,用到時才創建物件)

之後我們在透過 getShareInstance 拿到單例之後,我們就能以一般處理物件的方式操作它。不同的是,它在你的應用中是唯一的物件:

這邊我們不需要使用 getShareInstance 方式來給予單例物件,而是我們透過 shared 的靜態常數來提供訪問。如此一來也能達成同樣的操作:

如果對於 static let 或全域變數會在何時被創建,可以參考以下文章:https://developer.apple.com/swift/blog/?id=7

▸ 使用 Singleton 的方式

前面有提到說 Singtleton 就類似一種全域變數的概念,因此這邊介紹幾個能夠比較安全使用 Singleton 的方式。首先我們先建立一個用來查看登入狀態的 Singleton 稱為 LoginManger

隱藏 Singleton

首先我們先編寫一個 Loginable 的協議,並且不要求任何內容。接著,再透過 protocol extension 配合 LoginManger 單例來提供默認實作:

接著我們讓需要的物件遵循 Loginable 協議,就能使用其默認提供的 LoginManger 單例的實現:

觀察者模式(Observer Pattern)

我們可以配合我們之前觀察者模式的文章使用,首先先編寫一個 LoginManagerObserver 的協議,用來觀察 isLogin 的變動:

接著必須到 LoginManager 添加觀察者模式的相關程式碼:

最後我們使用上會像是這樣:

隱藏 setter,透過方法修改

因為 Singleton 類似於全域變數,因此容易被更動。所以,通常我對於需要暴露給外部的屬性會設置為 let 、計算屬性或 private(set) ,而透過 private(set) 標記的屬性,我們會暴露一個方法來提供修改。

透過這種方式可以防止意外的被修改,即使是被修改也比較容易除錯(有呼叫過程),也能夠再配合上面兩個方式使用。

--

--

Jeremy Xue
Jeremy Xue ‘s Blog

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