鍍金池/ 教程/ iOS/ 單例模式 - Singleton
開始
裝飾者模式 - Decorator
單例模式 - Singleton
外觀模式 - Facade
觀察者模式 - Observer
準(zhǔn)備工作
iOS 設(shè)計(jì)模式
適配器模式 - Adapter
備忘錄模式 - Memento
最后的潤(rùn)色
小結(jié)
設(shè)計(jì)模式之王- MVC

單例模式 - Singleton

單例模式確保每個(gè)指定的類只存在一個(gè)實(shí)例對(duì)象,并且可以全局訪問那個(gè)實(shí)例。一般情況下會(huì)使用延時(shí)加載的策略,只在第一次需要使用的時(shí)候初始化。

注意:在 iOS 中單例模式很常見,NSUserDefaults.standardUserDefaults()UIApplication.sharedApplication() 、 UIScreen.mainScreen() 、 NSFileManager.defaultManager() 這些都是單例模式。

你可能會(huì)疑惑了:如果多于一個(gè)實(shí)例又會(huì)怎么樣呢?代碼和內(nèi)存還沒精貴到這個(gè)地步吧?

某些場(chǎng)景下,保持實(shí)例對(duì)象僅有一份是很有意義的。舉個(gè)例子,你的應(yīng)用實(shí)例 (UIApplication),應(yīng)該只有一個(gè)吧,顯然是指你的當(dāng)前應(yīng)用。還有一個(gè)例子:設(shè)備的屏幕 (UIScreen) 實(shí)例也是這樣,所以對(duì)于這些類的情況,你只想要一個(gè)實(shí)例對(duì)象。

單例模式的應(yīng)用還有另一種情況:你需要一個(gè)全局類來處理配置文件。我們很容易通過單例模式實(shí)現(xiàn)線程安全的實(shí)例訪問,而如果有多個(gè)類可以同時(shí)訪問配置文件,那可就復(fù)雜多了。

如何使用單例模式

可以看下這個(gè)圖:

http://wiki.jikexueyuan.com/project/ios-design-patterns-in-swift/images/1.6.png" alt="" />

這是一個(gè)日志類,有一個(gè)屬性 (是一個(gè)單例對(duì)象) 和兩個(gè)方法 (sharedInstance()init())。

第一次調(diào)用 sharedInstance() 的時(shí)候,instance 屬性還沒有初始化。所以我們要?jiǎng)?chuàng)建一個(gè)新實(shí)例并且返回。

下一次你再調(diào)用 sharedInstance() 的時(shí)候,instance 已經(jīng)初始化完成,直接返回即可。這個(gè)邏輯確保了這個(gè)類只存在一個(gè)實(shí)例對(duì)象。

接下來我們繼續(xù)完善單例模式,通過這個(gè)類來管理專輯數(shù)據(jù)。

注意到在我們前面的截圖里,分組中有個(gè) API 分組,這里可以放那些提供后臺(tái)服務(wù)的類。在這個(gè)分組中創(chuàng)建一個(gè)新的文件 LibraryAPI.swift ,繼承自 NSObject 類。

LibraryAPI 里添加下面這段代碼:

//1
class var sharedInstance: LibraryAPI {
    //2
    struct Singleton {
        //3
        static let instance = LibraryAPI()
    }
    //4
    return Singleton.instance
}

在這幾行代碼里,做了如下工作:

  • 創(chuàng)建一個(gè)計(jì)算類型的類變量,這個(gè)類變量,就像是 objc 中的靜態(tài)方法一樣,可以直接通過類訪問而不用實(shí)例對(duì)象。具體可參見蘋果官方文檔的 [屬性][10] 這一章。
  • 在類變量里嵌套一個(gè) Singleton 結(jié)構(gòu)體。
  • Singleton 封裝了一個(gè)靜態(tài)的常量,通過 static 定義意味著這個(gè)屬性只存在一個(gè),注意 Swift 中 static 的變量是延時(shí)加載的,意味著 Instance 直到需要的時(shí)候才會(huì)被創(chuàng)建。同時(shí)再注意一下,因?yàn)樗且粋€(gè)常量,所以一旦創(chuàng)建之后不會(huì)再創(chuàng)建第二次。這些就是單例模式的核心所在:一旦初始化完成,當(dāng)前類存在一個(gè)實(shí)例對(duì)象,初始化方法就不會(huì)再被調(diào)用。
  • 返回計(jì)算后的屬性值。

注意:更多的單例模式實(shí)例可以看看 Github 上的這個(gè)[示例][11],列舉了單例模式的若干種實(shí)現(xiàn)方式。

你現(xiàn)在可以將這個(gè)單例作為專輯管理類的入口,接下來我們繼續(xù)創(chuàng)建一個(gè)處理專輯數(shù)據(jù)持久化的類。

新建 PersistencyManager.swift 并添加如下代碼:

private var albums = [Album]()

在這里我們定義了一個(gè)私有屬性,用來存儲(chǔ)專輯數(shù)據(jù)。這是一個(gè)可變數(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]
}

在這個(gè)初始化方法里,我們初始化了五張專輯。如果上面的專輯沒有你喜歡的,你可以隨意替換成你的菜:]

然后添加如下方法:

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ù)。

這時(shí)你可以運(yùn)行一下你的項(xiàng)目,確保編譯通過以便進(jìn)行下一步操作。

此時(shí)你或許會(huì)感到好奇: PersistencyManager 好像不是單例???是的,它確實(shí)不是單例。不過沒關(guān)系,在接下來的外觀模式章節(jié),你會(huì)看到 LibraryAPIPersistencyManager 之間的聯(lián)系。