首先咱們要明白下面三個問題:設計模式
單例模式(Singleton):單例模式確保對於一個給定的類只有一個實例存在,這個實例有一個全局惟一的訪問點。安全
例如:[NSUserDefaults standardUserDefaults],[UIApplication sharedApplication], [UIScreen mainScreen], [NSFileManager defaultManager]等,全部的這些方法都返回一個單例對象,蘋果公司大量使用了此模式。網絡
通常狀況下,項目中的工具類使用單例模式比較合適,工具類通常是整個項目都用的上,可是隻用一個,因此不必建立多個。工具
單例的實現應該分爲兩種狀況:非ARC(MRC)和ARC學習
MRC狀況下咱們的單例模式實現以下:spa
1> 在不使用單例模式的狀況下打印三個實例對象,他們指向的地址是不同的。示例代碼以下:線程
DHNetworkTool *tool1 = [[DHNetworkTool alloc]init];設計
DHNetworkTool *tool2 = [[DHNetworkTool alloc]init];code
DHNetworkTool *tool3 = [[DHNetworkTool alloc]init];對象
NSLog(@"\ntool1 = %p\ntool2 = %p\ntool3 = %p",tool1,tool2,tool3);
2> 咱們但願經過DHNetworkTool建立的對象是同一個,也就是隻分配一塊內存空間,那咱們應該去重寫alloc方法,由於alloc方法負責分配內存空間的。
+ (instancetype)alloc {} + (instancetype)allocWithZone:(struct _NSZone *)zone {}
如今發現有以上兩個方法,那麼咱們應該重寫哪一個alloc方法呢?個人建議是重寫後者也就是+ (instancetype)allocWithZone:(struct _NSZone *)zone {}
爲何?
由於alloc內部會調用allocWithZone,也就是說allocWithZone方法更底層。也就是說咱們實現alloc方法的話就只能攔截alloc方法,可是實現allocWithZone方法的話,任何內存分配的方法咱們都能攔截。
Zone的意思就是空間,當你調用這個方法的時候,系統會自動給你傳遞一塊內存空間,Zone就是系統分配給開發者APP的內存空間。
注意:在咱們實現allocWithZone能調用父類的方法嗎?不能!若是調用了就至關於咱們沒寫!!!咱們的目的就是不要使用父類的,咱們本身作本身的事情。
3> 咱們須要建立一個全局變量爲了避免讓別人訪問,咱們應該使用static修飾一下
1 //用static是爲了避免讓別人訪問這個變量 2 static DHNetworkTool *_networkTool = nil; 3 4 //alloc內部會調用allocWithZone,咱們實現alloc方法的話就只能攔截alloc方法,可是實現allocWithZone方法的話,任何內存分配的方法咱們都能攔截 5 + (instancetype)allocWithZone:(struct _NSZone *)zone { 6 7 //若是在這裏調用父類的方法至關於沒有重寫allocWithZone方法 8 // return [super allocWithZone:zone]; 9 10 //這樣判斷的話就能保證咱們返回的都是_networkTool這個對象了 11 if (_networkTool == nil) { 12 13 static dispatch_once_t onceToken; 14 dispatch_once(&onceToken, ^{//保證線程安全,並且這個代碼只會被執行一次 15 16 //在這裏能夠調用父類的方法了 17 _networkTool = [super allocWithZone:zone]; 18 }); 19 20 21 } 22 return _networkTool; 23 }
4> 咱們初步實現後能夠再次驗證是否建立的對象是同一個
打印結果能夠驗證是同一個對象,由於實例化對象的時候要調用allocWithZone方法,在該方法實例化對象的代碼只會走一次,因此保證每次實例化的對象都是同一個。
5> 在MRC環境下咱們這樣寫是不嚴謹的,由於還可能會調用release方法!若是調用了release方法那就意味着這個對象完了,下次在調用alloc方法的時候就沒辦法建立對象了,由於在allocWithZone方法中實例化對象的代碼只走一次。
爲了不這種狀況,咱們還須要重寫release方法,攔截對象被釋放。重寫release方法就是爲了保證整個程序都有這個單例對象。
//重寫release防止對象被釋放,由於對象一旦被釋放就不再能生成了。 - (oneway void)release { }
6> 除了release方法外,咱們還須要重寫retain方法。爲何呢?由於咱們是單例模式,這個對象只會被建立一次,那麼咱們就一直讓他的引用計數爲1,不要增長,不要讓其增長。那麼咱們還須要再重寫retainCount方法,返回1就行了。
1 //使單例對象引用計數不增長 2 -(instancetype)retain { 3 4 return self; 5 } 6 7 //使單例對象引用計數一致爲1 8 - (NSUInteger)retainCount { 9 10 return 1; 11 }
7> allocWithZone release retainCount三者不可缺一。
8> 單例模式已經基本實現了,最後一步就是咱們應該仿照系統實現一個類方法返回咱們的單例對象。
注意:若是直接返回對象的話,那麼這個對象就會一直爲空,因此須要在類方法裏調用alloc ini方法。可是每次都調用init方法的話,咱們的對象每次都要被初始化,因此要重寫init方法,保證這個單例對象只執行一次初始化。爲何不判斷對象爲空呢?由於調用init的時候要先執行alloc方法。
1 + (instancetype)sharedNetworkTool { 2 3 //直接返回這個對象意味着它一直爲空 4 // return _networkTool; 5 return [[self alloc] init]; 6 } 7 8 //每次都調用init方法的話,咱們的對象每次都要被初始化,因此要保證init只執行一次 9 -(instancetype)init { 10 11 static dispatch_once_t onceToken; 12 dispatch_once(&onceToken, ^{ 13 _networkTool = [super init]; 14 }); 15 return _networkTool; 16 }
最後再次驗證一下,打印結果以下:
DHNetworkTool *tool1 = [[DHNetworkTool alloc]init];
DHNetworkTool *tool2 = [[DHNetworkTool alloc]init];
DHNetworkTool *tool3 = [DHNetworkTool sharedNetworkTool];
NSLog(@"\ntool1 = %p\ntool2 = %p\ntool3 = %p",tool1,tool2,tool3);
ARC下單例模式的實現相對比較簡單,下面就只是展現.m文件部分源碼,不作贅述了。
1 static id _instance = nil; 2 3 + (id)allocWithZone:(struct _NSZone *)zone 4 { 5 if (_instance == nil) { 6 static dispatch_once_t onceToken; 7 dispatch_once(&onceToken, ^{ // 安全(這個代碼只會被調用一次) 8 _instance = [super allocWithZone:zone]; 9 }); 10 } 11 return _instance; 12 } 13 14 - (id)init 15 { 16 static dispatch_once_t onceToken; 17 dispatch_once(&onceToken, ^{ 18 _instance = [super init]; 19 }); 20 return _instance; 21 } 22 23 + (instancetype)sharedDataTool 24 { 25 return [[self alloc] init]; 26 }
最後一點,在公司項目開發過程當中咱們一般會把單例模式的實現抽取成一個宏,放到.PCH文件中,這樣方便項目組中的每一個人去使用,再次也作一下抽取和代碼的展現吧,能夠直接拿到工程中使用。
1 //實現單例設計模式 2 3 // .h文件的實現 4 #define SingletonH(methodName) + (instancetype)shared##methodName; 5 6 // .m文件的實現 7 #if __has_feature(objc_arc) // 是ARC 8 #define SingletonM(methodName) \ 9 static id _instace = nil; \ 10 + (id)allocWithZone:(struct _NSZone *)zone \ 11 { \ 12 if (_instace == nil) { \ 13 static dispatch_once_t onceToken; \ 14 dispatch_once(&onceToken, ^{ \ 15 _instace = [super allocWithZone:zone]; \ 16 }); \ 17 } \ 18 return _instace; \ 19 } \ 20 \ 21 - (id)init \ 22 { \ 23 static dispatch_once_t onceToken; \ 24 dispatch_once(&onceToken, ^{ \ 25 _instace = [super init]; \ 26 }); \ 27 return _instace; \ 28 } \ 29 \ 30 + (instancetype)shared##methodName \ 31 { \ 32 return [[self alloc] init]; \ 33 } \ 34 + (id)copyWithZone:(struct _NSZone *)zone \ 35 { \ 36 return _instace; \ 37 } \ 38 \ 39 + (id)mutableCopyWithZone:(struct _NSZone *)zone \ 40 { \ 41 return _instace; \ 42 } 43 44 #else // 不是ARC 45 46 #define SingletonM(methodName) \ 47 static id _instace = nil; \ 48 + (id)allocWithZone:(struct _NSZone *)zone \ 49 { \ 50 if (_instace == nil) { \ 51 static dispatch_once_t onceToken; \ 52 dispatch_once(&onceToken, ^{ \ 53 _instace = [super allocWithZone:zone]; \ 54 }); \ 55 } \ 56 return _instace; \ 57 } \ 58 \ 59 - (id)init \ 60 { \ 61 static dispatch_once_t onceToken; \ 62 dispatch_once(&onceToken, ^{ \ 63 _instace = [super init]; \ 64 }); \ 65 return _instace; \ 66 } \ 67 \ 68 + (instancetype)shared##methodName \ 69 { \ 70 return [[self alloc] init]; \ 71 } \ 72 \ 73 - (oneway void)release \ 74 { \ 75 \ 76 } \ 77 \ 78 - (id)retain \ 79 { \ 80 return self; \ 81 } \ 82 \ 83 - (NSUInteger)retainCount \ 84 { \ 85 return 1; \ 86 } \ 87 + (id)copyWithZone:(struct _NSZone *)zone \ 88 { \ 89 return _instace; \ 90 } \ 91 \ 92 + (id)mutableCopyWithZone:(struct _NSZone *)zone \ 93 { \ 94 return _instace; \ 95 } 96 97 #endif
寫了一個小小的Demo作簡單的展現,歡迎你們一塊兒學習交流哇。連接: http://pan.baidu.com/s/1bpHjYUF 密碼: ti7y