一般情況下,可能我們寫的單例模式是這樣的:
- #import <Foundation/Foundation.h>
- @interface Downloader : NSObject
- + (instancetype)sharedDownloader;
- @end
- #import "Downloader.h"
- @implementation Downloader
- static Downloader *_sharedDownloader = nil;
- + (instancetype)sharedDownloader {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _sharedDownloader = [[self alloc] init];
- });
- return _sharedDownloader;
- }
- @end
具體使用:
- #import <Foundation/Foundation.h>
- #import "Downloader.h"
- int main(int argc, const char * argv[]) {
- @autoreleasepool {
- Downloader *obj1 = [Downloader sharedDownloader];
- NSLog(@"obj1 = %@", obj1);
- Downloader *obj2 = [Downloader sharedDownloader];
- NSLog(@"obj2 = %@", obj2);
- Downloader *obj3 = [[Downloader alloc] init];
- NSLog(@"obj3 = %@", obj3);
- }
- return 0;
- }
輸出結果:
- obj1 = <Downloader: 0x78f25be0>
- obj2 = <Downloader: 0x78f25be0>
- obj3 = <Downloader: 0x79225e00>
可以看到,當我們調用sharedDownloader方法時獲取到的對象是相同的,但是當我們通過alloc和init來構造對象的時候,得到的對象卻是不一樣的。
那么問題就來了,我們通過不同的途徑得到不同的對象,顯然是不行的。我們必須要確保對象的唯一性,所以我們就需要封鎖用戶通過alloc和init以及copy來構造對象這條道路。
創建對象分為申請內存(alloc)、初始化(init)這兩個步驟,我們要確保對象的唯一性,因此在第一步這個階段我們就要攔截它。當我們調用alloc方法時,OC內部會調用allocWithZone這個方法來申請內存,我們覆寫這個方法,然后在這個方法中調用sharedDownloader方法返回單例對象,這樣就可以達到我們的目的。拷貝對象也是同樣的原理,覆寫copyWithZone方法,然后在這個方法中調用sharedDownloader方法返回單例對象。
- #import "Downloader.h"
- @implementation Downloader
- static Downloader *_sharedDownloader = nil;
- + (instancetype)sharedDownloader {
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- _sharedDownloader = [[super allocWithZone:NULL] init];
- });
- return _sharedDownloader;
- }
- + (id)allocWithZone:(struct _NSZone *)zone {
- return [Downloader sharedDownloader];
- }
- - (id)copyWithZone:(struct _NSZone *)zone {
- return [Downloader sharedDownloader];
- }
- @end
輸出結果:
- obj1 = <Downloader: 0x7ca42e70>
- obj2 = <Downloader: 0x7ca42e70>
- obj3 = <Downloader: 0x7ca42e70>
可以看到獲取到的對象都是一樣的了。