一、我們常用的單例有哪些?
[[UIApplication sharedApplication] statusBarStyle];//系統中的單例模式,通過它獲取到狀態欄的style
[NSNotificationCenter defaultCenter] addObserver:<#(nonnull id)#> selector:<#(nonnull SEL)#> name:<#(nullable NSString *)#> object:<#(nullable id)#>];//defaultCenter從控制中心類中獲取到了單例的實例
[NSUserDefaults standardUserDefaults] setObject:<#(nullable id)#> forKey:<#(nonnull NSString *)#>];
[NSFileManager defaultManager];
從這些常用的單例可以發現,通過這些常用的單例方法來獲取到這個類的唯一實例,再在這個實例的基礎上進行相關的操作、作業。
二、單例模式基本原理
單例模式,一般用來管理某些資源的,用來管理某個對象,他這個對象持有了某些核心資源,這個資源可以全局共享。大部分情況我們使用單例模式就是為了共享信息 ,一般作為管理中心。
缺點是因為他共享了信息,就破壞了設計模式中的最少知識原則,產生了耦合,破壞了封裝性,。但是在解決問題的過程中,這種不好的地方也是可以忽略的。
下面我們來自己寫一個單例的例子,詳細的講一下對單例的理解。創建一個UserInfoManagerCenter的類
仿照系統的單例形式自己寫這個類方法。
#import <Foundation/Foundation.h>
@interface UserInfoManagerCenter : NSObject
@property (nonatomic ,strong) NSString *name;
@property (nonatomic ,strong) NSString *age;
+ (instancetype)managerCenter;
@end
在UserInfoManagerCenter.m中實現這個方法
#import "UserInfoManagerCenter.h"
@implementation UserInfoManagerCenter
/**
* ?常規做法
*/
+(instancetype)managerCenter
{
? ?static UserInfoManagerCenter *center = nil;//靜態變量持有這個對象
? ?if (center == nil) {
? ? ? ?center = [[UserInfoManagerCenter alloc]init];
? ?}
? ?return center;
}
@end
但是這種方法并不好,當多個地方調用這個方法時,會造成同時都進入到alloc init。
因此,使用第二種方法
#import "UserInfoManagerCenter.h"
@implementation UserInfoManagerCenter
/**
* ?第二種方案,用dispatch_once來解決競爭問題
*/
+(instancetype)managerCenter
{
? ?static UserInfoManagerCenter *center = nil;
? ?static dispatch_once_t onceToken;
? ?dispatch_once(&onceToken, ^{
? ? ? ?center = [[UserInfoManagerCenter alloc]init];
? ?});
? ?return center;
}
@end
當然還有第三種方法 ? ?--- ??initialize的作用,同一個類初始化時只會調用一次。
#import "UserInfoManagerCenter.h"
static UserInfoManagerCenter *center = nil;
@implementation UserInfoManagerCenter
/**
* 第三種方法,每個類調用任意方法時都會提前調用的這個initialize方法,initialize的作用,同一個類初始化時只會調用一次。所以說我們將單例寫在這個地方也是沒有問題的,但是不推薦
*/
+(void)initialize
{
if (self == [UserInfoManagerCenter class]) {
center = [[UserInfoManagerCenter alloc]init];
}
}
+(instancetype)managerCenter
{
return center;
}
@end
讓我們來驗證下,然后在AppDelegate里賦值
#import "AppDelegate.h"
#import "UserInfoManagerCenter.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
UserInfoManagerCenter *center = [UserInfoManagerCenter managerCenter];
center.name = @"YUSIR";
return YES;
}
在ViewController的viewDidLoad里取出值查看結果
#import "ViewController.h"
#import "UserInfoManagerCenter.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
UserInfoManagerCenter * center = [UserInfoManagerCenter managerCenter];
NSLog(@"name:%@",center.name);
}
三種方法結果能打印出來,單例實現了資源共享
三、嚴格單例模式的注意點
下面來簡單談談嚴格的單例模式,有三個問題可能需要注意一下:
1.如何防止繼承;
2.如何確保實例對象只出現一個;
3.防止實例對象被釋放掉;
第一個問題,防止繼承
+(instancetype)managerCenter
{
static UserInfoManagerCenter *center = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
center = [[UserInfoManagerCenter alloc]init];
});
//防止子類重載調用使用
NSString *classString = NSStringFromClass([self class]);//獲取當前類的名字
if ([classString isEqualToString:@"UserInfoManagerCenter"] == NO) {
NSParameterAssert(nil); //填nil會導致程序崩潰
}
return center;
}
創建一個子類繼承自UserInfoManagerCenter,調用managerCenter會直接崩潰,比較簡單這里就不截圖了
第二個問題,如何確保實例對象只出現一個。除了類方法之類還有init方法,只能重寫他的init方法,來實現init方法失效
static UserInfoManagerCenter *center = nil;
@implementation UserInfoManagerCenter
+(instancetype)managerCenter
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
center = (UserInfoManagerCenter *)@"UserInfoManagerCenter";
center = [[UserInfoManagerCenter alloc]init];
});
//防止子類重載調用使用
NSString *classString = NSStringFromClass([self class]);//獲取當前類的名字
if ([classString isEqualToString:@"UserInfoManagerCenter"] == NO) {
NSParameterAssert(nil); //填nil會導致程序崩潰
}
return center;
}
- (instancetype)init {
NSString *string = (NSString *)center;
if ([string isKindOfClass:[NSString class]]== YES && [string isEqualToString:@"UserInfoManagerCenter"]) {
self = [super init];
if (self) {
//防止子類重載調用使用
NSString *classString = NSStringFromClass([self class]);//獲取當前類的名字
if ([classString isEqualToString:@"UserInfoManagerCenter"] == NO) {
NSParameterAssert(nil); //填nil會導致程序崩潰
}
}
return self;
}else {
return nil;
}
}
第三個,由于現在是項目是ARC開發的,是引用計數管理的。無法重載release,可以跳過這個問題。
當然嚴格的單例模式,只要注意避免類似情況發生,就可以不用過多考慮這些負擔了。