單例(單態)模式
單例設計模式確保對於一個給定的類隻有一個實例存在,這個實例有一個全局唯一的訪問點。它通常采用懶加載的方式在第一次用到實例的時候再去創建它。
註意:蘋果大量使用瞭此模式。例如:[NSUserDefaults standardUserDefaults], [UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager],所有的這些方法都返回一個單例對象。
你很可能會想為什麼這麼關心是否一個類有多個實例?畢竟代碼和內存都是廉價的,對嗎?
有一些情況下,隻有一個實例顯得非常合理。舉例來說,你不需要有多個Logger的實例,除非你想去寫多個日志文件。或者一個全局的配置處理類:實現線程安全的方式訪問共享實例是容易的,比如一個配置文件,有好多個類同時修改這個文件。
如何使用單例模式
首先來看看下面的圖:
上面的圖描述瞭一個有單一屬性(它就是單一實例)和sharedInstance,init兩個方法的類。
客戶端第一次發送sharedInstance消息的時候,instance屬性尚未被初始化,所以此時你需要創建一個新的實例,然後返回它的引用。
當你下一次調用sharedInstance的時候,instance不需要任何初始化可以立即返回。這個邏輯保證總是隻有一個實例。
你接下來將用這個模式來創建一個管理所有專輯數據的類。
你將註意到工程中有一個API的組,在這個組裡你可以放入給你應用提供服務的所有類。在此組中,用IOS\Cocoa Touch\Objective-C class 模板創建一個新類,命名它為LibraryAPI,設置父類為NSObject.
打開LibraryAPI.h,用如下代碼替換它的內容
- @interfaceLibraryAPI : NSObject
- + (LibraryAPI*)sharedInstance;
- @end
現在打開LibraryAPI.m,在@implementation 那一行後面插入下面的方法:
- + (LibraryAPI*)sharedInstance {
- // 1 static LibraryAPI *_sharedInstance = nil;
- // 2
- static dispatch_once_t oncePredicate;
- // 3 dispatch_once(&oncePredicate, ^{
- _sharedInstance = [[LibraryAPI alloc] init]; });
- return _sharedInstance; }
在這個簡短的方法中,有一些需要需要註意的點:
1.聲明一個靜態變量去保存類的實例,確保它在類中的全局可用性。
2.聲明一個靜態變量dispatch_once_t ,它確保初始化器代碼隻執行一次
3.使用Grand Central Dispatch(GCD)執行初始化LibraryAPI變量的block.這 正是單例模式的關鍵:一旦類已經被初始化,初始化器永遠不會再被調用。
下一次你調用sharedInstance的時候,dispatch_once塊中的代碼將不會執行(因為它已經被執行瞭一次),你將得到原先已經初始化好的實例。
你現在有一個單例的對象作為管理專輯數據的入口。咋們更進一步來創建一個處理資料庫數據持久化的類。
在API組中,使用iOS\Cocoa Touch\Objective-C class 模板 創建一個新類,命名它為PersistencyManager,設置父類為NSObject.
打開PersistencyManager.h 在文件頭部增加下面的導入語句:
#import “Album.h”
接下來,在PersistenceManager.h文件的@interface之後,增加下面的代碼:
- - (NSArray*)getAlbums; - (void)addAlbum:(Album*)album atIndex:(int)index;
- - (void)deleteAlbumAtIndex:(int)index;
上面是你需要處理專輯數據的方法的原型。
打開PersistencyManager.m文件,在@implementation行之前,增加下面的代碼:
- @interfacePersistencyManager () { // an array of all albums
- NSMutableArray *albums; }
上面增加瞭一個類擴張(class extension),這是另外一個增加私有方法和變量以至於外部類不會看到它們的方式。這裡,你申明瞭一個數組NSMutableArry 來保存專輯數據。這個數組是可變的方便你增加和刪除專輯。
現在在PersistencyManager.m文件中@implementation行之後增加如下代碼:
- - (id)init {
- self = [super init]; if (self) {
- // a dummy list of albums albums = [NSMutableArrayarrayWithArray:
- @[[[Album alloc] initWithTitle:@"Best of Bowie" artist:@"David Bowie" coverUrl:@"https://www.coversproject.com/static/thumbs/album/album_david%20bowie_best%20of%20bowie.png" year:@"1992"], [[Album alloc] initWithTitle:@"It's My Life" artist:@"No Doubt" coverUrl:@"https://www.coversproject.com/static/thumbs/album/album_no%20doubt_its%20my%20life%20%20bathwater.png" year:@"2003"],
- [[Album alloc] initWithTitle:@"Nothing Like The Sun" artist:@"Sting" coverUrl:@"https://www.coversproject.com/static/thumbs/album/album_sting_nothing%20like%20the%20sun.png" year:@"1999"], [[Album alloc] initWithTitle:@"Staring at the Sun" artist:@"U2" coverUrl:@"https://www.coversproject.com/static/thumbs/album/album_u2_staring%20at%20the%20sun.png" year:@"2000"],
- [[Album alloc] initWithTitle:@"American Pie" artist:@"Madonna" coverUrl:@"https://www.coversproject.com/static/thumbs/album/album_madonna_american%20pie.png" year:@"2000"]]]; }
- return self; }
在init中,你用五條樣例專輯填充數組。如果你不喜歡上面的專輯,你可以自由用你喜歡的專輯替換它們。
現在在PersistencyManager.m文件中增加下面的三個方法:
- - (NSArray*)getAlbums {
- return albums; }
- - (void)addAlbum:(Album*)album atIndex:(int)index
- { if (albums.count >= index)
- [albums insertObject:album atIndex:index]; else
- [albums addObject:album]; }
- - (void)deleteAlbumAtIndex:(int)index
- { [albums removeObjectAtIndex:index];
- }