單例模式確保每個指定的類只存在一個實(shí)例對象,并且可以全局訪問那個實(shí)例。一般情況下會使用延時加載的策略,只在第一次需要使用的時候初始化。
注意:在 iOS 中單例模式很常見,NSUserDefaults.standardUserDefaults()
、 UIApplication.sharedApplication()
、 UIScreen.mainScreen()
、 NSFileManager.defaultManager()
這些都是單例模式。
你可能會疑惑了:如果多于一個實(shí)例又會怎么樣呢?代碼和內(nèi)存還沒精貴到這個地步吧?
某些場景下,保持實(shí)例對象僅有一份是很有意義的。舉個例子,你的應(yīng)用實(shí)例 (UIApplication
),應(yīng)該只有一個吧,顯然是指你的當(dāng)前應(yīng)用。還有一個例子:設(shè)備的屏幕 (UIScreen
) 實(shí)例也是這樣,所以對于這些類的情況,你只想要一個實(shí)例對象。
單例模式的應(yīng)用還有另一種情況:你需要一個全局類來處理配置文件。我們很容易通過單例模式實(shí)現(xiàn)線程安全的實(shí)例訪問,而如果有多個類可以同時訪問配置文件,那可就復(fù)雜多了。
可以看下這個圖:
這是一個日志類,有一個屬性 (是一個單例對象) 和兩個方法 (sharedInstance()
和 init()
)。
第一次調(diào)用 sharedInstance()
的時候,instance
屬性還沒有初始化。所以我們要創(chuàng)建一個新實(shí)例并且返回。
下一次你再調(diào)用 sharedInstance()
的時候,instance
已經(jīng)初始化完成,直接返回即可。這個邏輯確保了這個類只存在一個實(shí)例對象。
接下來我們繼續(xù)完善單例模式,通過這個類來管理專輯數(shù)據(jù)。
注意到在我們前面的截圖里,分組中有個 API
分組,這里可以放那些提供后臺服務(wù)的類。在這個分組中創(chuàng)建一個新的文件 LibraryAPI.swift
,繼承自 NSObject
類。
在 LibraryAPI
里添加下面這段代碼:
//1
class var sharedInstance: LibraryAPI {
//2
struct Singleton {
//3
static let instance = LibraryAPI()
}
//4
return Singleton.instance
}
在這幾行代碼里,做了如下工作:
Singleton
結(jié)構(gòu)體。Singleton
封裝了一個靜態(tài)的常量,通過 static
定義意味著這個屬性只存在一個,注意 Swift 中 static
的變量是延時加載的,意味著 Instance
直到需要的時候才會被創(chuàng)建。同時再注意一下,因?yàn)樗且粋€常量,所以一旦創(chuàng)建之后不會再創(chuàng)建第二次。這些就是單例模式的核心所在:一旦初始化完成,當(dāng)前類存在一個實(shí)例對象,初始化方法就不會再被調(diào)用。注意:更多的單例模式實(shí)例可以看看 Github
上的這個[示例][11],列舉了單例模式的若干種實(shí)現(xiàn)方式。
你現(xiàn)在可以將這個單例作為專輯管理類的入口,接下來我們繼續(xù)創(chuàng)建一個處理專輯數(shù)據(jù)持久化的類。
新建 PersistencyManager.swift
并添加如下代碼:
private var albums = [Album]()
在這里我們定義了一個私有屬性,用來存儲專輯數(shù)據(jù)。這是一個可變數(shù)組,所以你可以很容易的增加或者刪除數(shù)據(jù)。
然后加上一些初始化的數(shù)據(jù):
override init() {
//Dummy list of albums
let album1 = Album(title: "Best of Bowie",
artist: "David Bowie",
genre: "Pop",
coverUrl: "http://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png",
year: "1992")
let album2 = Album(title: "It's My Life",
artist: "No Doubt",
genre: "Pop",
coverUrl: "http://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png",
year: "2003")
let album3 = Album(title: "Nothing Like The Sun",
artist: "Sting",
genre: "Pop",
coverUrl: "http://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png",
year: "1999")
let album4 = Album(title: "Staring at the Sun",
artist: "U2",
genre: "Pop",
coverUrl: "http://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png",
year: "2000")
let album5 = Album(title: "American Pie",
artist: "Madonna",
genre: "Pop",
coverUrl: "http://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png",
year: "2000")
albums = [album1, album2, album3, album4, album5]
}
在這個初始化方法里,我們初始化了五張專輯。如果上面的專輯沒有你喜歡的,你可以隨意替換成你的菜:]
然后添加如下方法:
func getAlbums() -> [Album] {
return albums
}
func addAlbum(album: Album, index: Int) {
if (albums.count >= index) {
albums.insert(album, atIndex: index)
} else {
albums.append(album)
}
}
func deleteAlbumAtIndex(index: Int) {
albums.removeAtIndex(index)
}
這些方法可以讓你自由的訪問、添加、刪除專輯數(shù)據(jù)。
這時你可以運(yùn)行一下你的項(xiàng)目,確保編譯通過以便進(jìn)行下一步操作。
此時你或許會感到好奇: PersistencyManager
好像不是單例啊?是的,它確實(shí)不是單例。不過沒關(guān)系,在接下來的外觀模式章節(jié),你會看到 LibraryAPI
和 PersistencyManager
之間的聯(lián)系。
更多建議: