設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。GoF提出了23種設計模式,本系列將使用Swift語言來實現這些設計模式
概述
通過共享已存在的物件,減少建立物件記憶體開銷的設計模式被稱作享元模式
享元模式Flyweight
和單例模式Singleton
像是一對孿生兄弟,二者的表現方式非常相似,但二者的存在目的卻不一樣:
- 單例模式
保證了整個應用宣告週期內,同一個物件只會存在一份記憶體,並且任何時間都能被訪問使用。 - 享元模式
如果存在可以複用的物件,那麼物件將被共享而不是建立新的物件
在iOS開發中,享元模式的最佳實踐就是UITableView
的複用機制——超出螢幕外的單元格統一被回收放到一個複用佇列之中,等待著需要新的單元格時進行復用。
實戰
筆者最近專案有一個需求,幾乎所有的資料都要儲存在本地。由於應用的特殊性,模組之間需要用到彼此的資料,如果使用單例模式來做,那麼同一時間佔用的記憶體是非常的大的,因此以享元模式的思想封裝了一個資料管理類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class DataManager { //MARK: - Variable private static var shareStorage = [String: AnyObject]() private var storeKey = "DefaultKey" private var storeData = [AnyObject]() var data: [AnyObject] { get { return storeData } } //MARK: - Operate func insert(data: AnyObject) { } func delete(at index: Int) { } func save() { } } |
筆者以資料模型的類名作為資料管理的關鍵字,因此建立一個私有的靜態字典用來儲存當前正在使用的資料。由於資料以加密的方式儲存在沙盒目錄下,在資料量足夠大的時候,從本地讀取這些資料會佔用大量的花銷,因此在資料管理物件被建立的時候需要判斷是否存在可複用的資料,如果不存在再從本地載入:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class DataManager { init() { initalizeData() } init(storeKey: String) { self.storeKey = storeKey initalizeData() } private func initalizeData() { if let data = DataManager.shareStorage[storeKey] { storeData = data as! [AnyObject] } else { loadData() DataManager.shareStorage[storeKey] = storeData as AnyObject? } } private func loadData() { // load data from local path } } |
ok,對於資料的複用已經完成了,剩下的問題是不可能讓字典一直儲存這些資料,否則直接使用單例要更加方便的多。對此,筆者使用了計數功能,保證資料可以在沒有使用的時候進行本地儲存然後釋放:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class DataManager { deinit { let count = DataManager.shareStorage[countKey()] as! Int if count == 1 { save() DataManager.shareStorage[storeKey] = nil } else { DataManager.shareStorage[countKey()] = (count - 1) as AnyObject? } } private func initalizeData() { if let data = DataManager.shareStorage[storeKey] { let count = DataManager.shareStorage[countKey()] as! Int DataManager.shareStorage[countKey()] = (count + 1) as AnyObject? storeData = data as! [AnyObject] } else { loadData() DataManager.shareStorage[countKey()] = 1 as AnyObject DataManager.shareStorage[storeKey] = storeData as AnyObject? } } private func countKey() -> String { return "\(storeKey)Count" } } |
上面的程式碼是初步的邏輯搭建,下一步還需要考慮執行緒安全等其他問題,這裡就不再寫出來了
總結
最開始接觸享元模式概念的時候,筆者是有些混亂的,也不清楚它和單例的區別。簡單來說,這是一種提供了一種擁有單例優點、以及改善了一部分單例缺點的設計模式,但是享元模式更加的複雜,在考慮到多執行緒的環境下,資料競爭要比單例激烈的多,也危險的多。
上一篇:Swift實戰-單例模式